* @link http://github.com/brandonwamboldt/utilphp/ Official Documentation
*/
class util
{
/**
* A constant representing the number of seconds in a minute, for
* making code more verbose
*
* @var integer
*/
const SECONDS_IN_A_MINUTE = 60;
/**
* A constant representing the number of seconds in an hour, for making
* code more verbose
*
* @var integer
*/
const SECONDS_IN_A_HOUR = 3600;
const SECONDS_IN_AN_HOUR = 3600;
/**
* A constant representing the number of seconds in a day, for making
* code more verbose
*
* @var integer
*/
const SECONDS_IN_A_DAY = 86400;
/**
* A constant representing the number of seconds in a week, for making
* code more verbose
*
* @var integer
*/
const SECONDS_IN_A_WEEK = 604800;
/**
* A constant representing the number of seconds in a month (30 days),
* for making code more verbose
*
* @var integer
*/
const SECONDS_IN_A_MONTH = 2592000;
/**
* A constant representing the number of seconds in a year (365 days),
* for making code more verbose
*
* @var integer
*/
const SECONDS_IN_A_YEAR = 31536000;
/**
* URL constants as defined in the PHP Manual under "Constants usable with
* http_build_url()".
*
* @see http://us2.php.net/manual/en/http.constants.php#http.constants.url
*/
const HTTP_URL_REPLACE = 1;
const HTTP_URL_JOIN_PATH = 2;
const HTTP_URL_JOIN_QUERY = 4;
const HTTP_URL_STRIP_USER = 8;
const HTTP_URL_STRIP_PASS = 16;
const HTTP_URL_STRIP_AUTH = 32;
const HTTP_URL_STRIP_PORT = 64;
const HTTP_URL_STRIP_PATH = 128;
const HTTP_URL_STRIP_QUERY = 256;
const HTTP_URL_STRIP_FRAGMENT = 512;
const HTTP_URL_STRIP_ALL = 1024;
/**
* A collapse icon, using in the dump_var function to allow collapsing
* an array or object
*
* @var string
*/
public static $icon_collapse = 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAMAAADXT/YiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MjlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNzFDNDQyNEMyQzkxMUUxOTU4MEM4M0UxRDA0MUVGNSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNzFDNDQyM0MyQzkxMUUxOTU4MEM4M0UxRDA0MUVGNSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NDlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3MjlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuF4AWkAAAA2UExURU9t2DBStczM/1h16DNmzHiW7iNFrypMvrnD52yJ4ezs7Onp6ejo6P///+Tk5GSG7D9h5SRGq0Q2K74AAAA/SURBVHjaLMhZDsAgDANRY3ZISnP/y1ZWeV+jAeuRSky6cKL4ryDdSggP8UC7r6GvR1YHxjazPQDmVzI/AQYAnFQDdVSJ80EAAAAASUVORK5CYII=';
/**
* A collapse icon, using in the dump_var function to allow collapsing
* an array or object
*
* @var string
*/
public static $icon_expand = 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAMAAADXT/YiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MTlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFQzZERTJDNEMyQzkxMUUxODRCQzgyRUNDMzZEQkZFQiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFQzZERTJDM0MyQzkxMUUxODRCQzgyRUNDMzZEQkZFQiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MzlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3MTlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkmDvWIAAABIUExURU9t2MzM/3iW7ubm59/f5urq85mZzOvr6////9ra38zMzObm5rfB8FZz5myJ4SNFrypMvjBStTNmzOvr+mSG7OXl8T9h5SRGq/OfqCEAAABKSURBVHjaFMlbEoAwCEPRULXF2jdW9r9T4czcyUdA4XWB0IgdNSybxU9amMzHzDlPKKu7Fd1e6+wY195jW0ARYZECxPq5Gn8BBgCr0gQmxpjKAwAAAABJRU5ErkJggg==';
private static $hasArray = false;
/**
* Map of special non-ASCII characters and suitable ASCII replacement
* characters.
*
* Part of the URLify.php Project
';
$done = array();
$html .= self::var_dump_plain($var, intval($expandLevel), 0, $done);
$html .= '';
if (self::$hasArray) {
$html = $jsCode . $html;
}
if (!$return) {
echo $html;
}
return $html;
}
/**
* Display a variable's contents using nice HTML formatting (Without
* the tag) and will properly display the values of variables
* like booleans and resources. Supports collapsable arrays and objects
* as well.
*
* @param mixed $var The variable to dump
* @return string
*/
public static function var_dump_plain($var, $expLevel, $depth = 0, $done = array())
{
$html = '';
if ($expLevel > 0) {
$expLevel--;
$setImg = 0;
$setStyle = 'display:inline;';
} elseif ($expLevel == 0) {
$setImg = 1;
$setStyle='display:none;';
} elseif ($expLevel < 0) {
$setImg = 0;
$setStyle = 'display:inline;';
}
if (is_bool($var)) {
$html .= 'bool(' . (($var) ? 'true' : 'false') . ')';
} elseif (is_int($var)) {
$html .= 'int(' . $var . ')';
} elseif (is_float($var)) {
$html .= 'float(' . $var . ')';
} elseif (is_string($var)) {
$html .= 'string(' . strlen($var) . ') "' . self::htmlentities($var) . '"';
} elseif (is_null($var)) {
$html .= 'NULL';
} elseif (is_resource($var)) {
$html .= 'resource("' . get_resource_type($var) . '") "' . $var . '"';
} elseif (is_array($var)) {
// Check for recursion
if ($depth > 0) {
foreach ($done as $prev) {
if ($prev === $var) {
$html .= 'array(' . count($var) . ') *RECURSION DETECTED*';
return $html;
}
}
// Keep track of variables we have already processed to detect recursion
$done[] = &$var;
}
self::$hasArray = true;
$uuid = 'include-php-' . uniqid() . mt_rand(1, 1000000);
$html .= (!empty($var) ? '
' : '') . 'array(' . count($var) . ')';
if (!empty($var)) {
$html .= '
[
';
$indent = 4;
$longest_key = 0;
foreach ($var as $key => $value) {
if (is_string($key)) {
$longest_key = max($longest_key, strlen($key) + 2);
} else {
$longest_key = max($longest_key, strlen($key));
}
}
foreach ($var as $key => $value) {
if (is_numeric($key)) {
$html .= str_repeat(' ', $indent) . str_pad($key, $longest_key, ' ');
} else {
$html .= str_repeat(' ', $indent) . str_pad('"' . self::htmlentities($key) . '"', $longest_key, ' ');
}
$html .= ' => ';
$value = explode('
', self::var_dump_plain($value, $expLevel, $depth + 1, $done));
foreach ($value as $line => $val) {
if ($line != 0) {
$value[$line] = str_repeat(' ', $indent * 2) . $val;
}
}
$html .= implode('
', $value) . '
';
}
$html .= ']';
}
} elseif (is_object($var)) {
// Check for recursion
foreach ($done as $prev) {
if ($prev === $var) {
$html .= 'object(' . get_class($var) . ') *RECURSION DETECTED*';
return $html;
}
}
// Keep track of variables we have already processed to detect recursion
$done[] = &$var;
self::$hasArray=true;
$uuid = 'include-php-' . uniqid() . mt_rand(1, 1000000);
$html .= '
object(' . get_class($var) . ')
[
';
$varArray = (array) $var;
$indent = 4;
$longest_key = 0;
foreach ($varArray as $key => $value) {
if (substr($key, 0, 2) == "\0*") {
unset($varArray[$key]);
$key = 'protected:' . substr($key, 3);
$varArray[$key] = $value;
} elseif (substr($key, 0, 1) == "\0") {
unset($varArray[$key]);
$key = 'private:' . substr($key, 1, strpos(substr($key, 1), "\0")) . ':' . substr($key, strpos(substr($key, 1), "\0") + 2);
$varArray[$key] = $value;
}
if (is_string($key)) {
$longest_key = max($longest_key, strlen($key) + 2);
} else {
$longest_key = max($longest_key, strlen($key));
}
}
foreach ($varArray as $key => $value) {
if (is_numeric($key)) {
$html .= str_repeat(' ', $indent) . str_pad($key, $longest_key, ' ');
} else {
$html .= str_repeat(' ', $indent) . str_pad('"' . self::htmlentities($key) . '"', $longest_key, ' ');
}
$html .= ' => ';
$value = explode('
', self::var_dump_plain($value, $expLevel, $depth + 1, $done));
foreach ($value as $line => $val) {
if ($line != 0) {
$value[$line] = str_repeat(' ', $indent * 2) . $val;
}
}
$html .= implode('
', $value) . '
';
}
$html .= ']';
}
return $html;
}
/**
* Converts any accent characters to their equivalent normal characters
* and converts any other non-alphanumeric characters to dashes, then
* converts any sequence of two or more dashes to a single dash. This
* function generates slugs safe for use as URLs, and if you pass true
* as the second parameter, it will create strings safe for use as CSS
* classes or IDs.
*
* @param string $string A string to convert to a slug
* @param string $separator The string to separate words with
* @param boolean $css_mode Whether or not to generate strings safe for
* CSS classes/IDs (Default to false)
* @return string
*/
public static function slugify($string, $separator = '-', $css_mode = false)
{
// Compatibility with 1.0.* parameter ordering for semvar
if ($separator === true || $separator === false) {
$css_mode = $separator;
$separator = '-';
// Raise deprecation error
trigger_error(
'util::slugify() now takes $css_mode as the third parameter, please update your code',
E_USER_DEPRECATED
);
}
$slug = preg_replace('/([^a-z0-9]+)/', $separator, strtolower(self::remove_accents($string)));
if ($css_mode) {
$digits = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
if (is_numeric(substr($slug, 0, 1))) {
$slug = $digits[substr($slug, 0, 1)] . substr($slug, 1);
}
}
return $slug;
}
/**
* Checks to see if a string is utf8 encoded.
*
* NOTE: This function checks for 5-Byte sequences, UTF8
* has Bytes Sequences with a maximum length of 4.
*
* Written by Tony Ferrara
*
* @param string $string The string to be checked
* @return boolean
*/
public static function seems_utf8($string)
{
if (function_exists('mb_check_encoding')) {
// If mbstring is available, this is significantly faster than
// using PHP regexps.
return mb_check_encoding($string, 'UTF-8');
}
// @codeCoverageIgnoreStart
return self::seemsUtf8Regex($string);
// @codeCoverageIgnoreEnd
}
/**
* A non-Mbstring UTF-8 checker.
*
* @param $string
* @return bool
*/
protected static function seemsUtf8Regex($string)
{
// Obtained from http://stackoverflow.com/a/11709412/430062 with permission.
$regex = '/(
[\xC0-\xC1] # Invalid UTF-8 Bytes
| [\xF5-\xFF] # Invalid UTF-8 Bytes
| \xE0[\x80-\x9F] # Overlong encoding of prior code point
| \xF0[\x80-\x8F] # Overlong encoding of prior code point
| [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
| [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
| [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
| (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
| (?
*
* @param string $brokenSerializedData
* @return string
*/
public static function fix_broken_serialization($brokenSerializedData)
{
$fixdSerializedData = preg_replace_callback('!s:(\d+):"(.*?)";!', function($matches) {
$snip = $matches[2];
return 's:' . strlen($snip) . ':"' . $snip . '";';
}, $brokenSerializedData);
return $fixdSerializedData;
}
/**
* Checks to see if the page is being server over SSL or not
*
* @return boolean
*/
public static function is_https()
{
return isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
}
/**
* Add or remove query arguments to the URL.
*
* @param mixed $newKey Either newkey or an associative array
* @param mixed $newValue Either newvalue or oldquery or uri
* @param mixed $uri URI or URL to append the queru/queries to.
* @return string
*/
public static function add_query_arg($newKey, $newValue = null, $uri = null)
{
// Was an associative array of key => value pairs passed?
if (is_array($newKey)) {
$newParams = $newKey;
// Was the URL passed as an argument?
if (!is_null($newValue)) {
$uri = $newValue;
} elseif (!is_null($uri)) {
$uri = $uri;
} else {
$uri = self::array_get($_SERVER['REQUEST_URI'], '');
}
} else {
$newParams = array($newKey => $newValue);
// Was the URL passed as an argument?
$uri = is_null($uri) ? self::array_get($_SERVER['REQUEST_URI'], '') : $uri;
}
// Parse the URI into it's components
$puri = parse_url($uri);
if (isset($puri['query'])) {
parse_str($puri['query'], $queryParams);
$queryParams = array_merge($queryParams, $newParams);
} elseif (isset($puri['path']) && strstr($puri['path'], '=') !== false) {
$puri['query'] = $puri['path'];
unset($puri['path']);
parse_str($puri['query'], $queryParams);
$queryParams = array_merge($queryParams, $newParams);
} else {
$queryParams = $newParams;
}
// Strip out any query params that are set to false
foreach ($queryParams as $param => $value) {
if ($value === false) {
unset($queryParams[$param]);
}
}
// Re-construct the query string
$puri['query'] = http_build_query($queryParams);
// Re-construct the entire URL
$nuri = self::http_build_url($puri);
// Make the URI consistent with our input
if ($nuri[0] === '/' && strstr($uri, '/') === false) {
$nuri = substr($nuri, 1);
}
if ($nuri[0] === '?' && strstr($uri, '?') === false) {
$nuri = substr($nuri, 1);
}
return rtrim($nuri, '?');
}
/**
* Removes an item or list from the query string.
*
* @param string|array $keys Query key or keys to remove.
* @param bool $uri When false uses the $_SERVER value
* @return string
*/
public static function remove_query_arg($keys, $uri = null)
{
if (is_array($keys)) {
return self::add_query_arg(array_combine($keys, array_fill(0, count($keys), null)), $uri);
}
return self::add_query_arg(array($keys => null), $uri);
}
/**
* Build a URL.
*
* The parts of the second URL will be merged into the first according to
* the flags argument.
*
* @author Jake Smith
* @see https://github.com/jakeasmith/http_build_url/
*
* @param mixed $url (part(s) of) an URL in form of a string or
* associative array like parse_url() returns
* @param mixed $parts same as the first argument
* @param int $flags a bitmask of binary or'ed HTTP_URL constants;
* HTTP_URL_REPLACE is the default
* @param array $new_url if set, it will be filled with the parts of the
* composed url like parse_url() would return
* @return string
*/
public static function http_build_url($url, $parts = array(), $flags = self::HTTP_URL_REPLACE, &$new_url = array())
{
is_array($url) || $url = parse_url($url);
is_array($parts) || $parts = parse_url($parts);
isset($url['query']) && is_string($url['query']) || $url['query'] = null;
isset($parts['query']) && is_string($parts['query']) || $parts['query'] = null;
$keys = array('user', 'pass', 'port', 'path', 'query', 'fragment');
// HTTP_URL_STRIP_ALL and HTTP_URL_STRIP_AUTH cover several other flags.
if ($flags & self::HTTP_URL_STRIP_ALL) {
$flags |= self::HTTP_URL_STRIP_USER | self::HTTP_URL_STRIP_PASS
| self::HTTP_URL_STRIP_PORT | self::HTTP_URL_STRIP_PATH
| self::HTTP_URL_STRIP_QUERY | self::HTTP_URL_STRIP_FRAGMENT;
} elseif ($flags & self::HTTP_URL_STRIP_AUTH) {
$flags |= self::HTTP_URL_STRIP_USER | self::HTTP_URL_STRIP_PASS;
}
// Schema and host are alwasy replaced
foreach (array('scheme', 'host') as $part) {
if (isset($parts[$part])) {
$url[$part] = $parts[$part];
}
}
if ($flags & self::HTTP_URL_REPLACE) {
foreach ($keys as $key) {
if (isset($parts[$key])) {
$url[$key] = $parts[$key];
}
}
} else {
if (isset($parts['path']) && ($flags & self::HTTP_URL_JOIN_PATH)) {
if (isset($url['path']) && substr($parts['path'], 0, 1) !== '/') {
$url['path'] = rtrim(
str_replace(basename($url['path']), '', $url['path']),
'/'
) . '/' . ltrim($parts['path'], '/');
} else {
$url['path'] = $parts['path'];
}
}
if (isset($parts['query']) && ($flags & self::HTTP_URL_JOIN_QUERY)) {
if (isset($url['query'])) {
parse_str($url['query'], $url_query);
parse_str($parts['query'], $parts_query);
$url['query'] = http_build_query(
array_replace_recursive(
$url_query,
$parts_query
)
);
} else {
$url['query'] = $parts['query'];
}
}
}
if (isset($url['path']) && substr($url['path'], 0, 1) !== '/') {
$url['path'] = '/' . $url['path'];
}
foreach ($keys as $key) {
$strip = 'HTTP_URL_STRIP_' . strtoupper($key);
if ($flags & constant('utilphp\\util::' . $strip)) {
unset($url[$key]);
}
}
$parsed_string = '';
if (isset($url['scheme'])) {
$parsed_string .= $url['scheme'] . '://';
}
if (isset($url['user'])) {
$parsed_string .= $url['user'];
if (isset($url['pass'])) {
$parsed_string .= ':' . $url['pass'];
}
$parsed_string .= '@';
}
if (isset($url['host'])) {
$parsed_string .= $url['host'];
}
if (isset($url['port'])) {
$parsed_string .= ':' . $url['port'];
}
if (!empty($url['path'])) {
$parsed_string .= $url['path'];
} else {
$parsed_string .= '/';
}
if (isset($url['query'])) {
$parsed_string .= '?' . $url['query'];
}
if (isset($url['fragment'])) {
$parsed_string .= '#' . $url['fragment'];
}
$new_url = $url;
return $parsed_string;
}
/**
* Converts many english words that equate to true or false to boolean.
*
* Supports 'y', 'n', 'yes', 'no' and a few other variations.
*
* @param string $string The string to convert to boolean
* @param bool $default The value to return if we can't match any
* yes/no words
* @return boolean
*/
public static function str_to_bool($string, $default = false)
{
$yes_words = 'affirmative|all right|aye|indubitably|most assuredly|ok|of course|okay|sure thing|y|yes+|yea|yep|sure|yeah|true|t|on|1|oui|vrai';
$no_words = 'no*|no way|nope|nah|na|never|absolutely not|by no means|negative|never ever|false|f|off|0|non|faux';
if (preg_match('/^(' . $yes_words . ')$/i', $string)) {
return true;
} elseif (preg_match('/^(' . $no_words . ')$/i', $string)) {
return false;
}
return $default;
}
/**
* Check if a string starts with the given string.
*
* @param string $string
* @param string $starts_with
* @return boolean
*/
public static function starts_with($string, $starts_with)
{
return strpos($string, $starts_with) === 0;
}
/**
* Check if a string ends with the given string.
*
* @param string $string
* @param string $starts_with
* @return boolean
*/
public static function ends_with($string, $ends_with)
{
return substr($string, -strlen($ends_with)) === $ends_with;
}
/**
* Check if a string contains another string.
*
* @param string $haystack
* @param string $needle
* @return boolean
*/
public static function str_contains($haystack, $needle)
{
return strpos($haystack, $needle) !== false;
}
/**
* Check if a string contains another string. This version is case
* insensitive.
*
* @param string $haystack
* @param string $needle
* @return boolean
*/
public static function str_icontains($haystack, $needle)
{
return stripos($haystack, $needle) !== false;
}
/**
* Return the file extension of the given filename.
*
* @param string $filename
* @return string
*/
public static function get_file_ext($filename)
{
return pathinfo($filename, PATHINFO_EXTENSION);
}
/**
* Removes a directory (and its contents) recursively.
*
* Contributed by Askar (ARACOOL)
*
* @param string $dir The directory to be deleted recursively
* @param bool $traverseSymlinks Delete contents of symlinks recursively
* @return bool
* @throws \RuntimeException
*/
public static function rmdir($dir, $traverseSymlinks = false)
{
if (!file_exists($dir)) {
return true;
} elseif (!is_dir($dir)) {
throw new \RuntimeException('Given path is not a directory');
}
if (!is_link($dir) || $traverseSymlinks) {
foreach (scandir($dir) as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$currentPath = $dir . '/' . $file;
if (is_dir($currentPath)) {
self::rmdir($currentPath, $traverseSymlinks);
} elseif (!unlink($currentPath)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to delete ' . $currentPath);
// @codeCoverageIgnoreEnd
}
}
}
// Windows treats removing directory symlinks identically to removing directories.
if (is_link($dir) && !defined('PHP_WINDOWS_VERSION_MAJOR')) {
if (!unlink($dir)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to delete ' . $dir);
// @codeCoverageIgnoreEnd
}
} else {
if (!rmdir($dir)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to delete ' . $dir);
// @codeCoverageIgnoreEnd
}
}
return true;
}
/**
* Convert entities, while preserving already-encoded entities.
*
* @param string $string The text to be converted
* @return string
*/
public static function htmlentities($string, $preserve_encoded_entities = false)
{
if ($preserve_encoded_entities) {
// @codeCoverageIgnoreStart
if (defined('HHVM_VERSION')) {
$translation_table = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
} else {
$translation_table = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES, self::mbInternalEncoding());
}
// @codeCoverageIgnoreEnd
$translation_table[chr(38)] = '&';
return preg_replace('/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/', '&', strtr($string, $translation_table));
}
return htmlentities($string, ENT_QUOTES, self::mbInternalEncoding());
}
/**
* Convert >, <, ', " and & to html entities, but preserves entities that
* are already encoded.
*
* @param string $string The text to be converted
* @return string
*/
public static function htmlspecialchars($string, $preserve_encoded_entities = false)
{
if ($preserve_encoded_entities) {
// @codeCoverageIgnoreStart
if (defined('HHVM_VERSION')) {
$translation_table = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
} else {
$translation_table = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES, self::mbInternalEncoding());
}
// @codeCoverageIgnoreEnd
$translation_table[chr(38)] = '&';
return preg_replace('/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/', '&', strtr($string, $translation_table));
}
return htmlentities($string, ENT_QUOTES, self::mbInternalEncoding());
}
/**
* Transliterates characters to their ASCII equivalents.
*
* Part of the URLify.php Project
*
* @see https://github.com/jbroadway/urlify/blob/master/URLify.php
*
* @param string $text Text that might have not-ASCII characters
* @param string $language Specifies a priority for a specific language.
* @return string Filtered string with replaced "nice" characters
*/
public static function downcode($text, $language = '')
{
self::initLanguageMap($language);
if (self::seems_utf8($text)) {
if (preg_match_all(self::$regex, $text, $matches)) {
for ($i = 0; $i < count($matches[0]); $i++) {
$char = $matches[0][$i];
if (isset(self::$map[$char])) {
$text = str_replace($char, self::$map[$char], $text);
}
}
}
} else {
// Not a UTF-8 string so we assume its ISO-8859-1
$search = "\x80\x83\x8a\x8e\x9a\x9e\x9f\xa2\xa5\xb5\xc0\xc1\xc2\xc3\xc4\xc5\xc7\xc8\xc9\xca\xcb\xcc\xcd";
$search .= "\xce\xcf\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xe0\xe1\xe2\xe3\xe4\xe5\xe7\xe8\xe9";
$search .= "\xea\xeb\xec\xed\xee\xef\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xff";
$text = strtr($text, $search, 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy');
// These latin characters should be represented by two characters so
// we can't use strtr
$complexSearch = array("\x8c", "\x9c", "\xc6", "\xd0", "\xde", "\xdf", "\xe6", "\xf0", "\xfe");
$complexReplace = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
$text = str_replace($complexSearch, $complexReplace, $text);
}
return $text;
}
/**
* Converts all accent characters to ASCII characters.
*
* If there are no accent characters, then the string given is just
* returned.
*
* @param string $string Text that might have accent characters
* @param string $language Specifies a priority for a specific language.
* @return string Filtered string with replaced "nice" characters
*/
public static function remove_accents($string, $language = '')
{
if (!preg_match('/[\x80-\xff]/', $string)) {
return $string;
}
return self::downcode($string, $language);
}
/**
* Strip all witespaces from the given string.
*
* @param string $string The string to strip
* @return string
*/
public static function strip_space($string)
{
return preg_replace('/\s+/', '', $string);
}
/**
* Sanitize a string by performing the following operation :
* - Remove accents
* - Lower the string
* - Remove punctuation characters
* - Strip whitespaces
*
* @param string $string the string to sanitize
* @return string
*/
public static function sanitize_string($string)
{
$string = self::remove_accents($string);
$string = strtolower($string);
$string = preg_replace('/[^a-zA-Z 0-9]+/', '', $string);
$string = self::strip_space($string);
return $string;
}
/**
* Pads a given string with zeroes on the left.
*
* @param int $number The number to pad
* @param int $length The total length of the desired string
* @return string
*/
public static function zero_pad($number, $length)
{
return str_pad($number, $length, '0', STR_PAD_LEFT);
}
/**
* Converts a unix timestamp to a relative time string, such as "3 days ago"
* or "2 weeks ago".
*
* @param int $from The date to use as a starting point
* @param int $to The date to compare to, defaults to now
* @param string $suffix The string to add to the end, defaults to " ago"
* @return string
*/
public static function human_time_diff($from, $to = '', $as_text = false, $suffix = ' ago')
{
if ($to == '') {
$to = time();
}
$from = new \DateTime(date('Y-m-d H:i:s', $from));
$to = new \DateTime(date('Y-m-d H:i:s', $to));
$diff = $from->diff($to);
if ($diff->y > 1) {
$text = $diff->y . ' years';
} elseif ($diff->y == 1) {
$text = '1 year';
} elseif ($diff->m > 1) {
$text = $diff->m . ' months';
} elseif ($diff->m == 1) {
$text = '1 month';
} elseif ($diff->d > 7) {
$text = ceil($diff->d / 7) . ' weeks';
} elseif ($diff->d == 7) {
$text = '1 week';
} elseif ($diff->d > 1) {
$text = $diff->d . ' days';
} elseif ($diff->d == 1) {
$text = '1 day';
} elseif ($diff->h > 1) {
$text = $diff->h . ' hours';
} elseif ($diff->h == 1) {
$text = ' 1 hour';
} elseif ($diff->i > 1) {
$text = $diff->i . ' minutes';
} elseif ($diff->i == 1) {
$text = '1 minute';
} elseif ($diff->s > 1) {
$text = $diff->s . ' seconds';
} else {
$text = '1 second';
}
if ($as_text) {
$text = explode(' ', $text, 2);
$text = self::number_to_word($text[0]) . ' ' . $text[1];
}
return trim($text) . $suffix;
}
/**
* Converts a number into the text equivalent. For example, 456 becomes four
* hundred and fifty-six.
*
* Part of the IntToWords Project.
*
* @param int|float $number The number to convert into text
* @return string
*/
public static function number_to_word($number)
{
$number = (string) $number;
if (strpos($number, '.') !== false) {
list($number, $decimal) = explode('.', $number);
} else {
$decimal = false;
}
$output = '';
if ($number[0] == '-') {
$output = 'negative ';
$number = ltrim($number, '-');
} elseif ($number[0] == '+') {
$output = 'positive ';
$number = ltrim($number, '+');
}
if ($number[0] == '0') {
$output .= 'zero';
} else {
$length = 19;
$number = str_pad($number, 60, '0', STR_PAD_LEFT);
$group = rtrim(chunk_split($number, 3, ' '), ' ');
$groups = explode(' ', $group);
$groups2 = array();
foreach ($groups as $group) {
$group[1] = isset($group[1]) ? $group[1] : null;
$group[2] = isset($group[2]) ? $group[2] : null;
$groups2[] = self::numberToWordThreeDigits($group[0], $group[1], $group[2]);
}
for ($z = 0; $z < count($groups2); $z++) {
if ($groups2[$z] != '') {
$output .= $groups2[$z] . self::numberToWordConvertGroup($length - $z);
$output .= ($z < $length && ! array_search('', array_slice($groups2, $z + 1, -1)) && $groups2[$length] != '' && $groups[$length][0] == '0' ? ' and ' : ', ');
}
}
$output = rtrim($output, ', ');
}
if ($decimal > 0) {
$output .= ' point';
for ($i = 0; $i < strlen($decimal); $i++) {
$output .= ' ' . self::numberToWordConvertDigit($decimal[$i]);
}
}
return $output;
}
protected static function numberToWordConvertGroup($index)
{
switch($index) {
case 11:
return ' decillion';
case 10:
return ' nonillion';
case 9:
return ' octillion';
case 8:
return ' septillion';
case 7:
return ' sextillion';
case 6:
return ' quintrillion';
case 5:
return ' quadrillion';
case 4:
return ' trillion';
case 3:
return ' billion';
case 2:
return ' million';
case 1:
return ' thousand';
case 0:
return '';
}
return '';
}
protected static function numberToWordThreeDigits($digit1, $digit2, $digit3)
{
$output = '';
if ($digit1 == '0' && $digit2 == '0' && $digit3 == '0') {
return '';
}
if ($digit1 != '0') {
$output .= self::numberToWordConvertDigit($digit1) . ' hundred';
if ($digit2 != '0' || $digit3 != '0') {
$output .= ' and ';
}
}
if ($digit2 != '0') {
$output .= self::numberToWordTwoDigits($digit2, $digit3);
} elseif ($digit3 != '0') {
$output .= self::numberToWordConvertDigit($digit3);
}
return $output;
}
protected static function numberToWordTwoDigits($digit1, $digit2)
{
if ($digit2 == '0') {
switch ($digit1) {
case '1':
return 'ten';
case '2':
return 'twenty';
case '3':
return 'thirty';
case '4':
return 'forty';
case '5':
return 'fifty';
case '6':
return 'sixty';
case '7':
return 'seventy';
case '8':
return 'eighty';
case '9':
return 'ninety';
}
} elseif ($digit1 == '1') {
switch ($digit2) {
case '1':
return 'eleven';
case '2':
return 'twelve';
case '3':
return 'thirteen';
case '4':
return 'fourteen';
case '5':
return 'fifteen';
case '6':
return 'sixteen';
case '7':
return 'seventeen';
case '8':
return 'eighteen';
case '9':
return 'nineteen';
}
} else {
$second_digit = self::numberToWordConvertDigit($digit2);
switch ($digit1) {
case '2':
return "twenty-{$second_digit}";
case '3':
return "thirty-{$second_digit}";
case '4':
return "forty-{$second_digit}";
case '5':
return "fifty-{$second_digit}";
case '6':
return "sixty-{$second_digit}";
case '7':
return "seventy-{$second_digit}";
case '8':
return "eighty-{$second_digit}";
case '9':
return "ninety-{$second_digit}";
}
}
}
/**
* @param $digit
* @return string
* @throws \LogicException
*/
protected static function numberToWordConvertDigit($digit)
{
switch ($digit) {
case '0':
return 'zero';
case '1':
return 'one';
case '2':
return 'two';
case '3':
return 'three';
case '4':
return 'four';
case '5':
return 'five';
case '6':
return 'six';
case '7':
return 'seven';
case '8':
return 'eight';
case '9':
return 'nine';
default:
throw new \LogicException('Not a number');
}
}
/**
* Transmit UTF-8 content headers if the headers haven't already been sent.
*
* @param string $content_type The content type to send out
* @return boolean
*/
public static function utf8_headers($content_type = 'text/html')
{
// @codeCoverageIgnoreStart
if (!headers_sent()) {
header('Content-type: ' . $content_type . '; charset=utf-8');
return true;
}
return false;
// @codeCoverageIgnoreEnd
}
/**
* Transmit headers that force a browser to display the download file
* dialog. Cross browser compatible. Only fires if headers have not
* already been sent.
*
* @param string $filename The name of the filename to display to
* browsers
* @param string $content The content to output for the download.
* If you don't specify this, just the
* headers will be sent
* @return boolean
*/
public static function force_download($filename, $content = false)
{
// @codeCoverageIgnoreStart
if (!headers_sent()) {
// Required for some browsers
if (ini_get('zlib.output_compression')) {
@ini_set('zlib.output_compression', 'Off');
}
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
// Required for certain browsers
header('Cache-Control: private', false);
header('Content-Disposition: attachment; filename="' . basename(str_replace('"', '', $filename)) . '";');
header('Content-Type: application/force-download');
header('Content-Transfer-Encoding: binary');
if ($content) {
header('Content-Length: ' . strlen($content));
}
ob_clean();
flush();
if ($content) {
echo $content;
}
return true;
}
return false;
// @codeCoverageIgnoreEnd
}
/**
* Sets the headers to prevent caching for the different browsers.
*
* Different browsers support different nocache headers, so several
* headers must be sent so that all of them get the point that no
* caching should occur
*
* @return boolean
*/
public static function nocache_headers()
{
// @codeCoverageIgnoreStart
if (!headers_sent()) {
header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
return true;
}
return false;
// @codeCoverageIgnoreEnd
}
/**
* Generates a string of random characters.
*
* @throws LengthException If $length is bigger than the available
* character pool and $no_duplicate_chars is
* enabled
*
* @param integer $length The length of the string to
* generate
* @param boolean $human_friendly Whether or not to make the
* string human friendly by
* removing characters that can be
* confused with other characters (
* O and 0, l and 1, etc)
* @param boolean $include_symbols Whether or not to include
* symbols in the string. Can not
* be enabled if $human_friendly is
* true
* @param boolean $no_duplicate_chars Whether or not to only use
* characters once in the string.
* @return string
*/
public static function random_string($length = 16, $human_friendly = true, $include_symbols = false, $no_duplicate_chars = false)
{
$nice_chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefhjkmnprstuvwxyz23456789';
$all_an = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
$symbols = '!@#$%^&*()~_-=+{}[]|:;<>,.?/"\'\\`';
$string = '';
// Determine the pool of available characters based on the given parameters
if ($human_friendly) {
$pool = $nice_chars;
} else {
$pool = $all_an;
if ($include_symbols) {
$pool .= $symbols;
}
}
if (!$no_duplicate_chars) {
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
// Don't allow duplicate letters to be disabled if the length is
// longer than the available characters
if ($no_duplicate_chars && strlen($pool) < $length) {
throw new \LengthException('$length exceeds the size of the pool and $no_duplicate_chars is enabled');
}
// Convert the pool of characters into an array of characters and
// shuffle the array
$pool = str_split($pool);
$poolLength = count($pool);
$rand = mt_rand(0, $poolLength - 1);
// Generate our string
for ($i = 0; $i < $length; $i++) {
$string .= $pool[$rand];
// Remove the character from the array to avoid duplicates
array_splice($pool, $rand, 1);
// Generate a new number
if (($poolLength - 2 - $i) > 0) {
$rand = mt_rand(0, $poolLength - 2 - $i);
} else {
$rand = 0;
}
}
return $string;
}
/**
* Generate secure random string of given length
* If 'openssl_random_pseudo_bytes' is not available
* then generate random string using default function
*
* Part of the Laravel Project
*
* @param int $length length of string
* @return bool
*/
public static function secure_random_string($length = 16)
{
if (function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes($length * 2);
if ($bytes === false) {
throw new \LengthException('$length is not accurate, unable to generate random string');
}
return substr(str_replace(array('/', '+', '='), '', base64_encode($bytes)), 0, $length);
}
// @codeCoverageIgnoreStart
return static::random_string($length);
// @codeCoverageIgnoreEnd
}
/**
* Check if a given string matches a given pattern.
*
* Contributed by Abhimanyu Sharma
*
* @param string $pattern Parttern of string exptected
* @param string $string String that need to be matched
* @return bool
*/
public static function match_string($pattern, $string, $caseSensitive = true)
{
if ($pattern == $string) {
return true;
}
// Preg flags
$flags = $caseSensitive ? '' : 'i';
// Escape any regex special characters
$pattern = preg_quote($pattern, '#');
// Unescape * which is our wildcard character and change it to .*
$pattern = str_replace('\*', '.*', $pattern);
return (bool) preg_match('#^' . $pattern . '$#' . $flags, $string);
}
/**
* Validate an email address.
*
* @param string $possible_email An email address to validate
* @return bool
*/
public static function validate_email($possible_email)
{
return (bool) filter_var($possible_email, FILTER_VALIDATE_EMAIL);
}
/**
* Return the URL to a user's gravatar.
*
* @param string $email The email of the user
* @param integer $size The size of the gravatar
* @return string
*/
public static function get_gravatar($email, $size = 32)
{
if (self::is_https()) {
$url = 'https://secure.gravatar.com/';
} else {
$url = 'http://www.gravatar.com/';
}
$url .= 'avatar/' . md5($email) . '?s=' . (int) abs($size);
return $url;
}
/**
* Turns all of the links in a string into HTML links.
*
* Part of the LinkifyURL Project
*
* @param string $text The string to parse
* @return string
*/
public static function linkify($text)
{
$text = preg_replace('/'/', ''', $text); // IE does not handle ' entity!
$section_html_pattern = '%# Rev:20100913_0900 github.com/jmrware/LinkifyURL
# Section text into HTML tags and everything else.
( # $1: Everything not HTML tag.
[^<]+(?:(?!... tag.
]*> # opening tag.
[^<]*(?:(?! # closing tag.
) # End $2:
%ix';
return preg_replace_callback($section_html_pattern, array(__CLASS__, 'linkifyCallback'), $text);
}
/**
* Callback for the preg_replace in the linkify() method.
*
* Part of the LinkifyURL Project
*
* @param array $matches Matches from the preg_ function
* @return string
*/
protected static function linkifyRegex($text)
{
$url_pattern = '/# Rev:20100913_0900 github.com\/jmrware\/LinkifyURL
# Match http & ftp URL that is not already linkified.
# Alternative 1: URL delimited by (parentheses).
(\() # $1 "(" start delimiter.
((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $2: URL.
(\)) # $3: ")" end delimiter.
| # Alternative 2: URL delimited by [square brackets].
(\[) # $4: "[" start delimiter.
((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $5: URL.
(\]) # $6: "]" end delimiter.
| # Alternative 3: URL delimited by {curly braces}.
(\{) # $7: "{" start delimiter.
((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $8: URL.
(\}) # $9: "}" end delimiter.
| # Alternative 4: URL delimited by .
(<|&(?:lt|\#60|\#x3c);) # $10: "<" start delimiter (or HTML entity).
((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $11: URL.
(>|&(?:gt|\#62|\#x3e);) # $12: ">" end delimiter (or HTML entity).
| # Alternative 5: URL not delimited by (), [], {} or <>.
(# $13: Prefix proving URL not already linked.
(?: ^ # Can be a beginning of line or string, or
| [^=\s\'"\]] # a non-"=", non-quote, non-"]", followed by
) \s*[\'"]? # optional whitespace and optional quote;
| [^=\s]\s+ # or... a non-equals sign followed by whitespace.
) # End $13. Non-prelinkified-proof prefix.
(\b # $14: Other non-delimited URL.
(?:ht|f)tps?:\/\/ # Required literal http, https, ftp or ftps prefix.
[a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]+ # All URI chars except "&" (normal*).
(?: # Either on a "&" or at the end of URI.
(?! # Allow a "&" char only if not start of an...
&(?:gt|\#0*62|\#x0*3e); # HTML ">" entity, or
| &(?:amp|apos|quot|\#0*3[49]|\#x0*2[27]); # a [&\'"] entity if
[.!&\',:?;]? # followed by optional punctuation then
(?:[^a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]|$) # a non-URI char or EOS.
) & # If neg-assertion true, match "&" (special).
[a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]* # More non-& URI chars (normal*).
)* # Unroll-the-loop (special normal*)*.
[a-z0-9\-_~$()*+=\/#[\]@%] # Last char can\'t be [.!&\',;:?]
) # End $14. Other non-delimited URL.
/imx';
$url_replace = '$1$4$7$10$13$2$5$8$11$14$3$6$9$12';
return preg_replace($url_pattern, $url_replace, $text);
}
/**
* Callback for the preg_replace in the linkify() method.
*
* Part of the LinkifyURL Project
*
* @param array $matches Matches from the preg_ function
* @return string
*/
protected static function linkifyCallback($matches)
{
if (isset($matches[2])) {
return $matches[2];
}
return self::linkifyRegex($matches[1]);
}
/**
* Return the current URL.
*
* @return string
*/
public static function get_current_url()
{
$url = '';
// Check to see if it's over https
$is_https = self::is_https();
if ($is_https) {
$url .= 'https://';
} else {
$url .= 'http://';
}
// Was a username or password passed?
if (isset($_SERVER['PHP_AUTH_USER'])) {
$url .= $_SERVER['PHP_AUTH_USER'];
if (isset($_SERVER['PHP_AUTH_PW'])) {
$url .= ':' . $_SERVER['PHP_AUTH_PW'];
}
$url .= '@';
}
// We want the user to stay on the same host they are currently on,
// but beware of security issues
// see http://shiflett.org/blog/2006/mar/server-name-versus-http-host
$url .= $_SERVER['HTTP_HOST'];
$port = $_SERVER['SERVER_PORT'];
// Is it on a non standard port?
if ($is_https && ($port != 443)) {
$url .= ':' . $_SERVER['SERVER_PORT'];
} elseif (!$is_https && ($port != 80)) {
$url .= ':' . $_SERVER['SERVER_PORT'];
}
// Get the rest of the URL
if (!isset($_SERVER['REQUEST_URI'])) {
// Microsoft IIS doesn't set REQUEST_URI by default
$url .= $_SERVER['PHP_SELF'];
if (isset($_SERVER['QUERY_STRING'])) {
$url .= '?' . $_SERVER['QUERY_STRING'];
}
} else {
$url .= $_SERVER['REQUEST_URI'];
}
return $url;
}
/**
* Returns the IP address of the client.
*
* @param boolean $trust_proxy_headers Whether or not to trust the
* proxy headers HTTP_CLIENT_IP
* and HTTP_X_FORWARDED_FOR. ONLY
* use if your server is behind a
* proxy that sets these values
* @return string
*/
public static function get_client_ip($trust_proxy_headers = false)
{
if (!$trust_proxy_headers) {
return $_SERVER['REMOTE_ADDR'];
}
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
/**
* Truncate a string to a specified length without cutting a word off.
*
* @param string $string The string to truncate
* @param integer $length The length to truncate the string to
* @param string $append Text to append to the string IF it gets
* truncated, defaults to '...'
* @return string
*/
public static function safe_truncate($string, $length, $append = '...')
{
$ret = substr($string, 0, $length);
$last_space = strrpos($ret, ' ');
if ($last_space !== false && $string != $ret) {
$ret = substr($ret, 0, $last_space);
}
if ($ret != $string) {
$ret .= $append;
}
return $ret;
}
/**
* Truncate the string to given length of charactes.
*
* @param $string
* @param $limit
* @param string $append
* @return string
*/
public static function limit_characters($string, $limit = 100, $append = '...')
{
if (mb_strlen($string) <= $limit) {
return $string;
}
return rtrim(mb_substr($string, 0, $limit, 'UTF-8')) . $append;
}
/**
* Truncate the string to given length of words.
*
* @param $string
* @param $limit
* @param string $append
* @return string
*/
public static function limit_words($string, $limit = 100, $append = '...')
{
preg_match('/^\s*+(?:\S++\s*+){1,' . $limit . '}/u', $string, $matches);
if (!isset($matches[0]) || strlen($string) === strlen($matches[0])) {
return $string;
}
return rtrim($matches[0]).$append;
}
/**
* Returns the ordinal version of a number (appends th, st, nd, rd).
*
* @param string $number The number to append an ordinal suffix to
* @return string
*/
public static function ordinal($number)
{
$test_c = abs($number) % 10;
$ext = ((abs($number) % 100 < 21 && abs($number) % 100 > 4) ? 'th' : (($test_c < 4) ? ($test_c < 3) ? ($test_c < 2) ? ($test_c < 1) ? 'th' : 'st' : 'nd' : 'rd' : 'th'));
return $number . $ext;
}
/**
* Returns the file permissions as a nice string, like -rw-r--r-- or false
* if the file is not found.
*
* @param string $file The name of the file to get permissions form
* @param int $perms Numerical value of permissions to display as text.
* @return string
*/
public static function full_permissions($file, $perms = null)
{
if (is_null($perms)) {
if (!file_exists($file)) {
return false;
}
$perms = fileperms($file);
}
if (($perms & 0xC000) == 0xC000) {
// Socket
$info = 's';
} elseif (($perms & 0xA000) == 0xA000) {
// Symbolic Link
$info = 'l';
} elseif (($perms & 0x8000) == 0x8000) {
// Regular
$info = '-';
} elseif (($perms & 0x6000) == 0x6000) {
// Block special
$info = 'b';
} elseif (($perms & 0x4000) == 0x4000) {
// Directory
$info = 'd';
} elseif (($perms & 0x2000) == 0x2000) {
// Character special
$info = 'c';
} elseif (($perms & 0x1000) == 0x1000) {
// FIFO pipe
$info = 'p';
} else {
// Unknown
$info = 'u';
}
// Owner
$info .= (($perms & 0x0100) ? 'r' : '-');
$info .= (($perms & 0x0080) ? 'w' : '-');
$info .= (($perms & 0x0040) ?
(($perms & 0x0800) ? 's' : 'x') :
(($perms & 0x0800) ? 'S' : '-'));
// Group
$info .= (($perms & 0x0020) ? 'r' : '-');
$info .= (($perms & 0x0010) ? 'w' : '-');
$info .= (($perms & 0x0008) ?
(($perms & 0x0400) ? 's' : 'x') :
(($perms & 0x0400) ? 'S' : '-'));
// World
$info .= (($perms & 0x0004) ? 'r' : '-');
$info .= (($perms & 0x0002) ? 'w' : '-');
$info .= (($perms & 0x0001) ?
(($perms & 0x0200) ? 't' : 'x') :
(($perms & 0x0200) ? 'T' : '-'));
return $info;
}
/**
* Returns the first element in an array.
*
* @param array $array
* @return mixed
*/
public static function array_first(array $array)
{
return reset($array);
}
/**
* Returns the last element in an array.
*
* @param array $array
* @return mixed
*/
public static function array_last(array $array)
{
return end($array);
}
/**
* Returns the first key in an array.
*
* @param array $array
* @return int|string
*/
public static function array_first_key(array $array)
{
reset($array);
return key($array);
}
/**
* Returns the last key in an array.
*
* @param array $array
* @return int|string
*/
public static function array_last_key(array $array)
{
end($array);
return key($array);
}
/**
* Flatten a multi-dimensional array into a one dimensional array.
*
* Contributed by Theodore R. Smith of PHP Experts, Inc.
*
* @param array $array The array to flatten
* @param boolean $preserve_keys Whether or not to preserve array keys.
* Keys from deeply nested arrays will
* overwrite keys from shallowy nested arrays
* @return array
*/
public static function array_flatten(array $array, $preserve_keys = true)
{
$flattened = array();
array_walk_recursive($array, function($value, $key) use (&$flattened, $preserve_keys) {
if ($preserve_keys && !is_int($key)) {
$flattened[$key] = $value;
} else {
$flattened[] = $value;
}
});
return $flattened;
}
/**
* Accepts an array, and returns an array of values from that array as
* specified by $field. For example, if the array is full of objects
* and you call util::array_pluck($array, 'name'), the function will
* return an array of values from $array[]->name.
*
* @param array $array An array
* @param string $field The field to get values from
* @param boolean $preserve_keys Whether or not to preserve the
* array keys
* @param boolean $remove_nomatches If the field doesn't appear to be set,
* remove it from the array
* @return array
*/
public static function array_pluck(array $array, $field, $preserve_keys = true, $remove_nomatches = true)
{
$new_list = array();
foreach ($array as $key => $value) {
if (is_object($value)) {
if (isset($value->{$field})) {
if ($preserve_keys) {
$new_list[$key] = $value->{$field};
} else {
$new_list[] = $value->{$field};
}
} elseif (!$remove_nomatches) {
$new_list[$key] = $value;
}
} else {
if (isset($value[$field])) {
if ($preserve_keys) {
$new_list[$key] = $value[$field];
} else {
$new_list[] = $value[$field];
}
} elseif (!$remove_nomatches) {
$new_list[$key] = $value;
}
}
}
return $new_list;
}
/**
* Searches for a given value in an array of arrays, objects and scalar
* values. You can optionally specify a field of the nested arrays and
* objects to search in.
*
* @param array $array The array to search
* @param scalar $search The value to search for
* @param string $field The field to search in, if not specified
* all fields will be searched
* @return boolean|scalar False on failure or the array key on success
*/
public static function array_search_deep(array $array, $search, $field = false)
{
// *grumbles* stupid PHP type system
$search = (string) $search;
foreach ($array as $key => $elem) {
// *grumbles* stupid PHP type system
$key = (string) $key;
if ($field) {
if (is_object($elem) && $elem->{$field} === $search) {
return $key;
} elseif (is_array($elem) && $elem[$field] === $search) {
return $key;
} elseif (is_scalar($elem) && $elem === $search) {
return $key;
}
} else {
if (is_object($elem)) {
$elem = (array) $elem;
if (in_array($search, $elem)) {
return $key;
}
} elseif (is_array($elem) && in_array($search, $elem)) {
return $key;
} elseif (is_scalar($elem) && $elem === $search) {
return $key;
}
}
}
return false;
}
/**
* Returns an array containing all the elements of arr1 after applying
* the callback function to each one.
*
* @param string $callback Callback function to run for each
* element in each array
* @param array $array An array to run through the callback
* function
* @param boolean $on_nonscalar Whether or not to call the callback
* function on nonscalar values
* (Objects, resources, etc)
* @return array
*/
public static function array_map_deep(array $array, $callback, $on_nonscalar = false)
{
foreach ($array as $key => $value) {
if (is_array($value)) {
$args = array($value, $callback, $on_nonscalar);
$array[$key] = call_user_func_array(array(__CLASS__, __FUNCTION__), $args);
} elseif (is_scalar($value) || $on_nonscalar) {
$array[$key] = call_user_func($callback, $value);
}
}
return $array;
}
public static function array_clean(array $array)
{
return array_filter($array);
}
/**
* Wrapper to prevent errors if the user doesn't have the mbstring
* extension installed.
*
* @param string $encoding
* @return string
*/
protected static function mbInternalEncoding($encoding = null)
{
if (function_exists('mb_internal_encoding')) {
return $encoding ? mb_internal_encoding($encoding) : mb_internal_encoding();
}
// @codeCoverageIgnoreStart
return 'UTF-8';
// @codeCoverageIgnoreEnd
}
/**
* Set the writable bit on a file to the minimum value that allows the user
* running PHP to write to it.
*
* @param string $filename The filename to set the writable bit on
* @param boolean $writable Whether to make the file writable or not
* @return boolean
*/
public static function set_writable($filename, $writable = true)
{
$stat = @stat($filename);
if ($stat === false) {
return false;
}
// We're on Windows
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
return true;
}
list($myuid, $mygid) = array(posix_geteuid(), posix_getgid());
if ($writable) {
// Set only the user writable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, fileperms($filename) | 0200);
}
// Set only the group writable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, fileperms($filename) | 0220);
}
// Set the world writable bit (file isn't owned or grouped by us)
return chmod($filename, fileperms($filename) | 0222);
} else {
// Set only the user writable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, (fileperms($filename) | 0222) ^ 0222);
}
// Set only the group writable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, (fileperms($filename) | 0222) ^ 0022);
}
// Set the world writable bit (file isn't owned or grouped by us)
return chmod($filename, (fileperms($filename) | 0222) ^ 0002);
}
}
/**
* Set the readable bit on a file to the minimum value that allows the user
* running PHP to read to it.
*
* @param string $filename The filename to set the readable bit on
* @param boolean $readable Whether to make the file readable or not
* @return boolean
*/
public static function set_readable($filename, $readable = true)
{
$stat = @stat($filename);
if ($stat === false) {
return false;
}
// We're on Windows
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
return true;
}
list($myuid, $mygid) = array(posix_geteuid(), posix_getgid());
if ($readable) {
// Set only the user readable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, fileperms($filename) | 0400);
}
// Set only the group readable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, fileperms($filename) | 0440);
}
// Set the world readable bit (file isn't owned or grouped by us)
return chmod($filename, fileperms($filename) | 0444);
} else {
// Set only the user readable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, (fileperms($filename) | 0444) ^ 0444);
}
// Set only the group readable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, (fileperms($filename) | 0444) ^ 0044);
}
// Set the world readable bit (file isn't owned or grouped by us)
return chmod($filename, (fileperms($filename) | 0444) ^ 0004);
}
}
/**
* Set the executable bit on a file to the minimum value that allows the
* user running PHP to read to it.
*
* @param string $filename The filename to set the executable bit on
* @param boolean $executable Whether to make the file executable or not
* @return boolean
*/
public static function set_executable($filename, $executable = true)
{
$stat = @stat($filename);
if ($stat === false) {
return false;
}
// We're on Windows
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
return true;
}
list($myuid, $mygid) = array(posix_geteuid(), posix_getgid());
if ($executable) {
// Set only the user readable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, fileperms($filename) | 0100);
}
// Set only the group readable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, fileperms($filename) | 0110);
}
// Set the world readable bit (file isn't owned or grouped by us)
return chmod($filename, fileperms($filename) | 0111);
} else {
// Set only the user readable bit (file is owned by us)
if ($stat['uid'] == $myuid) {
return chmod($filename, (fileperms($filename) | 0111) ^ 0111);
}
// Set only the group readable bit (file group is the same as us)
if ($stat['gid'] == $mygid) {
return chmod($filename, (fileperms($filename) | 0111) ^ 0011);
}
// Set the world readable bit (file isn't owned or grouped by us)
return chmod($filename, (fileperms($filename) | 0111) ^ 0001);
}
}
/**
* Returns size of a given directory in bytes.
*
* @param string $dir
* @return integer
*/
public static function directory_size($dir)
{
$size = 0;
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS)) as $file => $key) {
if ($key->isFile()) {
$size += $key->getSize();
}
}
return $size;
}
/**
* Returns a home directory of current user.
*
* @return string
*/
public static function get_user_directory()
{
if (isset($_SERVER['HOMEDRIVE'])) return $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
else return $_SERVER['HOME'];
}
/**
* Returns all paths inside a directory.
*
* @param string $dir
* @return array
*/
public static function directory_contents($dir)
{
$contents = array();
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS)) as $pathname => $fi) {
$contents[] = $pathname;
}
natsort($contents);
return $contents;
}
}