/com/misc.php
PHP | 3899 lines | 3041 code | 219 blank | 639 comment | 297 complexity | c856600b7d0b0471aede00c3723d951b MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * Copyright 2011 Unirgy LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package BuckyBall
- * @link http://github.com/unirgy/buckyball
- * @author Boris Gurvich <boris@unirgy.com>
- * @copyright (c) 2010-2012 Boris Gurvich
- * @license http://www.apache.org/licenses/LICENSE-2.0.html
- */
- /**
- * Utility class to parse and construct strings and data structures
- */
- class BUtil extends BClass
- {
- /**
- * IV for mcrypt operations
- *
- * @var string
- */
- protected static $_mcryptIV;
- /**
- * Encryption key from configuration (encrypt/key)
- *
- * @var string
- */
- protected static $_mcryptKey;
- /**
- * Default hash algorithm
- *
- * @var string default sha512 for strength and slowness
- */
- protected static $_hashAlgo = 'bcrypt';
- /**
- * Default number of hash iterations
- *
- * @var int
- */
- protected static $_hashIter = 3;
- /**
- * Default full hash string separator
- *
- * @var string
- */
- protected static $_hashSep = '$';
- /**
- * Default character pool for random and sequence strings
- *
- * Chars "c", "C" are ommited to avoid accidental obscene language
- * Chars "0", "1", "I" are removed to avoid leading 0 and ambiguity in print
- *
- * @var string
- */
- protected static $_defaultCharPool = '23456789abdefghijklmnopqrstuvwxyzABDEFGHJKLMNOPQRSTUVWXYZ';
- /**
- * Shortcut to help with IDE autocompletion
- *
- * @return BUtil
- */
- public static function i($new=false, array $args=array())
- {
- return BClassRegistry::i()->instance(__CLASS__, $args, !$new);
- }
- /**
- * Convert any data to JSON string
- *
- * $data can be BData instance, or array of BModel objects, will be automatically converted to array
- *
- * @param mixed $data
- * @return string
- */
- public static function toJson($data)
- {
- if (is_array($data) && is_object(current($data)) && current($data) instanceof BModel) {
- $data = BDb::many_as_array($data);
- } elseif (is_object($data) && $data instanceof BData) {
- $data = $data->as_array(true);
- }
- return json_encode($data);
- }
- /**
- * Parse JSON into PHP data
- *
- * @param string $json
- * @param bool $asObject if false will attempt to convert to array,
- * otherwise standard combination of objects and arrays
- */
- public static function fromJson($json, $asObject=false)
- {
- $obj = json_decode($json);
- return $asObject ? $obj : static::objectToArray($obj);
- }
- /**
- * Indents a flat JSON string to make it more human-readable.
- *
- * @param string $json The original JSON string to process.
- *
- * @return string Indented version of the original JSON string.
- */
- public static function jsonIndent($json)
- {
- $result = '';
- $pos = 0;
- $strLen = strlen($json);
- $indentStr = ' ';
- $newLine = "\n";
- $prevChar = '';
- $outOfQuotes = true;
- for ($i=0; $i<=$strLen; $i++) {
- // Grab the next character in the string.
- $char = substr($json, $i, 1);
- // Are we inside a quoted string?
- if ($char == '"' && $prevChar != '\\') {
- $outOfQuotes = !$outOfQuotes;
- // If this character is the end of an element,
- // output a new line and indent the next line.
- } else if(($char == '}' || $char == ']') && $outOfQuotes) {
- $result .= $newLine;
- $pos --;
- for ($j=0; $j<$pos; $j++) {
- $result .= $indentStr;
- }
- }
- // Add the character to the result string.
- $result .= $char;
- // If the last character was the beginning of an element,
- // output a new line and indent the next line.
- if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
- $result .= $newLine;
- if ($char == '{' || $char == '[') {
- $pos ++;
- }
- for ($j = 0; $j < $pos; $j++) {
- $result .= $indentStr;
- }
- }
- $prevChar = $char;
- }
- return $result;
- }
- /**
- * Convert data to JavaScript string
- *
- * Notable difference from toJson: allows raw function callbacks
- *
- * @param mixed $val
- * @return string
- */
- public static function toJavaScript($val)
- {
- if (is_null($val)) {
- return 'null';
- } elseif (is_bool($val)) {
- return $val ? 'true' : 'false';
- } elseif (is_string($val)) {
- if (preg_match('#^\s*function\s*\(#', $val)) {
- return $val;
- } else {
- return "'".addslashes($val)."'";
- }
- } elseif (is_int($val) || is_float($val)) {
- return $val;
- } elseif ($val instanceof BValue) {
- return $val->toPlain();
- } elseif (($isObj = is_object($val)) || is_array($val)) {
- $out = array();
- if (!empty($val) && ($isObj || array_keys($val) !== range(0, count($val)-1))) { // assoc?
- foreach ($val as $k=>$v) {
- $out[] = "'".addslashes($k)."':".static::toJavaScript($v);
- }
- return '{'.join(',', $out).'}';
- } else {
- foreach ($val as $k=>$v) {
- $out[] = static::toJavaScript($v);
- }
- return '['.join(',', $out).']';
- }
- }
- return '"UNSUPPORTED TYPE"';
- }
- public static function toRss($data)
- {
- $lang = !empty($data['language']) ? $data['language'] : 'en-us';
- $ttl = !empty($data['ttl']) ? (int)$data['ttl'] : 40;
- $descr = !empty($data['description']) ? $data['description'] : $data['title'];
- $xml = '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel>'
- .'<title><![CDATA['.$data['title'].']]></title><link><![CDATA['.$data['link'].']]></link>'
- .'<description><![CDATA['.$descr.']]></description><language><![CDATA['.$lang.']]></language><ttl>'.$ttl.'</ttl>';
- foreach ($data['items'] as $item) {
- if (!is_numeric($item['pubDate'])) {
- $item['pubDate'] = strtotime($item['pubDate']);
- }
- if (empty($item['guid'])) {
- $item['guid'] = $item['link'];
- }
- $xml .= '<item><title><![CDATA['.$item['title'].']]></title>'
- .'<description><![CDATA['.$item['description'].']]></description>'
- .'<pubDate>'.date('r', $item['pubDate']).'</pubDate>'
- .'<guid><![CDATA['.$item['guid'].']]></guid><link><![CDATA['.$item['link'].']]></link></item>';
- }
- $xml .= '</channel></rss>';
- return $xml;
- }
- /**
- * Convert object to array recursively
- *
- * @param object $d
- * @return array
- */
- public static function objectToArray($d)
- {
- if (is_object($d)) {
- $d = get_object_vars($d);
- }
- if (is_array($d)) {
- return array_map('BUtil::objectToArray', $d);
- }
- return $d;
- }
- /**
- * Convert array to object
- *
- * @param mixed $d
- * @return object
- */
- public static function arrayToObject($d)
- {
- if (is_array($d)) {
- return (object) array_map('BUtil::objectToArray', $d);
- }
- return $d;
- }
- /**
- * version of sprintf for cases where named arguments are desired (php syntax)
- *
- * with sprintf: sprintf('second: %2$s ; first: %1$s', '1st', '2nd');
- *
- * with sprintfn: sprintfn('second: %second$s ; first: %first$s', array(
- * 'first' => '1st',
- * 'second'=> '2nd'
- * ));
- *
- * @see http://www.php.net/manual/en/function.sprintf.php#94608
- * @param string $format sprintf format string, with any number of named arguments
- * @param array $args array of [ 'arg_name' => 'arg value', ... ] replacements to be made
- * @return string|false result of sprintf call, or bool false on error
- */
- public static function sprintfn($format, $args = array())
- {
- $args = (array)$args;
- // map of argument names to their corresponding sprintf numeric argument value
- $arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);
- // find the next named argument. each search starts at the end of the previous replacement.
- for ($pos = 0; preg_match('/(?<=%)([a-zA-Z_]\w*)(?=\$)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
- $arg_pos = $match[0][1];
- $arg_len = strlen($match[0][0]);
- $arg_key = $match[1][0];
- // programmer did not supply a value for the named argument found in the format string
- if (! array_key_exists($arg_key, $arg_nums)) {
- user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
- return false;
- }
- // replace the named argument with the corresponding numeric one
- $format = substr_replace($format, $replace = $arg_nums[$arg_key], $arg_pos, $arg_len);
- $pos = $arg_pos + strlen($replace); // skip to end of replacement for next iteration
- }
- if (!$args) {
- $args = array('');
- }
- return vsprintf($format, array_values($args));
- }
- /**
- * Inject vars into string template
- *
- * Ex: echo BUtil::injectVars('One :two :three', array('two'=>2, 'three'=>3))
- * Result: "One 2 3"
- *
- * @param string $str
- * @param array $vars
- * @return string
- */
- public static function injectVars($str, $vars)
- {
- $from = array(); $to = array();
- foreach ($vars as $k=>$v) {
- $from[] = ':'.$k;
- $to[] = $v;
- }
- return str_replace($from, $to, $str);
- }
- /**
- * Merges any number of arrays / parameters recursively, replacing
- * entries with string keys with values from latter arrays.
- * If the entry or the next value to be assigned is an array, then it
- * automagically treats both arguments as an array.
- * Numeric entries are appended, not replaced, but only if they are
- * unique
- *
- * calling: result = BUtil::arrayMerge(a1, a2, ... aN)
- *
- * @param array $array1
- * @param array $array2...
- * @return array
- **/
- public static function arrayMerge() {
- $arrays = func_get_args();
- $base = array_shift($arrays);
- if (!is_array($base)) {
- $base = empty($base) ? array() : array($base);
- }
- foreach ($arrays as $append) {
- if (!is_array($append)) {
- $append = array($append);
- }
- foreach ($append as $key => $value) {
- if (is_numeric($key)) {
- if (!in_array($value, $base)) {
- $base[] = $value;
- }
- } elseif (!array_key_exists($key, $base)) {
- $base[$key] = $value;
- } elseif (is_array($value) && is_array($base[$key])) {
- $base[$key] = static::arrayMerge($base[$key], $append[$key]);
- } else {
- $base[$key] = $value;
- }
- }
- }
- return $base;
- }
- /**
- * Compare 2 arrays recursively
- *
- * @param array $array1
- * @param array $array2
- */
- public static function arrayCompare(array $array1, array $array2)
- {
- $diff = false;
- // Left-to-right
- foreach ($array1 as $key => $value) {
- if (!array_key_exists($key,$array2)) {
- $diff[0][$key] = $value;
- } elseif (is_array($value)) {
- if (!is_array($array2[$key])) {
- $diff[0][$key] = $value;
- $diff[1][$key] = $array2[$key];
- } else {
- $new = static::arrayCompare($value, $array2[$key]);
- if ($new !== false) {
- if (isset($new[0])) $diff[0][$key] = $new[0];
- if (isset($new[1])) $diff[1][$key] = $new[1];
- }
- }
- } elseif ($array2[$key] !== $value) {
- $diff[0][$key] = $value;
- $diff[1][$key] = $array2[$key];
- }
- }
- // Right-to-left
- foreach ($array2 as $key => $value) {
- if (!array_key_exists($key,$array1)) {
- $diff[1][$key] = $value;
- }
- // No direct comparsion because matching keys were compared in the
- // left-to-right loop earlier, recursively.
- }
- return $diff;
- }
- /**
- * Walk over array of objects and perform method or callback on each row
- *
- * @param array $arr
- * @param callback $cb
- * @param array $args
- * @param boolean $ignoreExceptions
- * @return array
- */
- static public function arrayWalk($arr, $cb, $args=array(), $ignoreExceptions=false)
- {
- $result = array();
- foreach ($arr as $i=>$r) {
- $callback = is_string($cb) && $cb[0]==='.' ? array($r, substr($cb, 1)) : $cb;
- if ($ignoreExceptions) {
- try {
- $result[] = call_user_func_array($callback, $args);
- } catch (Exception $e) {
- BDebug::warning('EXCEPTION class('.get_class($r).') arrayWalk('.$i.'): '.$e->getMessage());
- }
- } else {
- $result[] = call_user_func_array($callback, $args);
- }
- }
- return $result;
- }
- /**
- * Clean array of ints from empty and non-numeric values
- *
- * If parameter is a string, splits by comma
- *
- * @param array|string $arr
- * @return array
- */
- static public function arrayCleanInt($arr)
- {
- $res = array();
- if (is_string($arr)) {
- $arr = explode(',', $arr);
- }
- if (is_array($arr)) {
- foreach ($arr as $k=>$v) {
- if (is_numeric($v)) {
- $res[$k] = intval($v);
- }
- }
- }
- return $res;
- }
- /**
- * Insert 1 or more items into array at specific position
- *
- * Note: code repetition is for better iteration performance
- *
- * @param array $array The original container array
- * @param array $items Items to be inserted
- * @param string $where
- * - start
- * - end
- * - offset==$key
- * - key.(before|after)==$key
- * - obj.(before|after).$object_property==$key
- * - arr.(before|after).$item_array_key==$key
- * @return array resulting array
- */
- static public function arrayInsert($array, $items, $where)
- {
- $result = array();
- $w1 = explode('==', $where, 2);
- $w2 = explode('.', $w1[0], 3);
- switch ($w2[0]) {
- case 'start':
- $result = array_merge($items, $array);
- break;
- case 'end':
- $result = array_merge($array, $items);
- break;
- case 'offset': // for associative only
- $key = $w1[1];
- $i = 0;
- foreach ($array as $k=>$v) {
- if ($key===$i++) {
- foreach ($items as $k1=>$v1) {
- $result[$k1] = $v1;
- }
- }
- $result[$k] = $v;
- }
- break;
- case 'key': // for associative only
- $rel = $w2[1];
- $key = $w1[1];
- foreach ($array as $k=>$v) {
- if ($key===$k) {
- if ($rel==='after') {
- $result[$k] = $v;
- }
- foreach ($items as $k1=>$v1) {
- $result[$k1] = $v1;
- }
- if ($rel==='before') {
- $result[$k] = $v;
- }
- } else {
- $result[$k] = $v;
- }
- }
- break;
- case 'obj':
- $rel = $w2[1];
- $f = $w2[2];
- $key = $w1[1];
- foreach ($array as $k=>$v) {
- if ($key===$v->$f) {
- if ($rel==='after') {
- $result[$k] = $v;
- }
- foreach ($items as $k1=>$v1) {
- $result[$k1] = $v1;
- }
- if ($rel==='before') {
- $result[$k] = $v;
- }
- } else {
- $result[$k] = $v;
- }
- }
- break;
- case 'arr':
- $rel = $w2[1];
- $f = $w2[2];
- $key = $w1[1];
- foreach ($array as $k=>$v) {
- if ($key===$v[$f]) {
- if ($rel==='after') {
- $result[$k] = $v;
- }
- foreach ($items as $k1=>$v1) {
- $result[$k1] = $v1;
- }
- if ($rel==='before') {
- $result[$k] = $v;
- }
- } else {
- $result[$k] = $v;
- }
- }
- break;
- default: BDebug::error('Invalid where condition: '.$where);
- }
- return $result;
- }
- /**
- * Return only specific fields from source array
- *
- * @param array $source
- * @param array|string $fields
- * @param boolean $inverse if true, will return anything NOT in $fields
- * @param boolean $setNulls fill missing fields with nulls
- * @result array
- */
- static public function arrayMask(array $source, $fields, $inverse=false, $setNulls=true)
- {
- if (is_string($fields)) {
- $fields = explode(',', $fields);
- array_walk($fields, 'trim');
- }
- $result = array();
- if (!$inverse) {
- foreach ($fields as $k) {
- if (isset($source[$k])) {
- $result[$k] = $source[$k];
- } elseif ($setNulls) {
- $result[$k] = null;
- }
- }
- } else {
- foreach ($source as $k=>$v) {
- if (!in_array($k, $fields)) $result[$k] = $v;
- }
- }
- return $result;
- }
- static public function arrayToOptions($source, $labelField, $keyField=null, $emptyLabel=null)
- {
- $options = array();
- if (!is_null($emptyLabel)) {
- $options = array("" => $emptyLabel);
- }
- if (empty($source)) {
- return array();
- }
- $isObject = is_object(current($source));
- foreach ($source as $k=>$item) {
- if ($isObject) {
- $key = is_null($keyField) ? $k : $item->$keyField;
- $label = $labelField[0]==='.' ? $item->{substr($labelField, 1)}() : $item->labelField;
- $options[$key] = $label;
- } else {
- $key = is_null($keyField) ? $k : $item[$keyField];
- $options[$key] = $item[$labelField];
- }
- }
- return $options;
- }
- static public function arrayMakeAssoc($source, $keyField)
- {
- $isObject = is_object(current($source));
- $assocArray = array();
- foreach ($source as $k => $item) {
- if ($isObject) {
- $assocArray[$item->$keyField] = $item;
- } else {
- $assocArray[$item[$keyField]] = $item;
- }
- }
- return $assocArray;
- }
- /**
- * Create IV for mcrypt operations
- *
- * @return string
- */
- static public function mcryptIV()
- {
- if (!static::$_mcryptIV) {
- static::$_mcryptIV = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_DEV_URANDOM);
- }
- return static::$_mcryptIV;
- }
- /**
- * Fetch default encryption key from config
- *
- * @return string
- */
- static public function mcryptKey($key=null, $configPath=null)
- {
- if (!is_null($key)) {
- static::$_mcryptKey = $key;
- } elseif (is_null(static::$_mcryptKey) && $configPath) {
- static::$_mcryptKey = BConfig::i()->get($configPath);
- }
- return static::$_mcryptKey;
- }
- /**
- * Encrypt using AES256
- *
- * Requires PHP extension mcrypt
- *
- * @param string $value
- * @param string $key
- * @param boolean $base64
- * @return string
- */
- static public function encrypt($value, $key=null, $base64=true)
- {
- if (is_null($key)) $key = static::mcryptKey();
- $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $value, MCRYPT_MODE_ECB, static::mcryptIV());
- return $base64 ? trim(base64_encode($enc)) : $enc;
- }
- /**
- * Decrypt using AES256
- *
- * Requires PHP extension mcrypt
- *
- * @param string $value
- * @param string $key
- * @param boolean $base64
- * @return string
- */
- static public function decrypt($value, $key=null, $base64=true)
- {
- if (is_null($key)) $key = static::mcryptKey();
- $enc = $base64 ? base64_decode($value) : $value;
- return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $enc, MCRYPT_MODE_ECB, static::mcryptIV()));
- }
- /**
- * Generate random string
- *
- * @param int $strLen length of resulting string
- * @param string $chars allowed characters to be used
- */
- public static function randomString($strLen=8, $chars=null)
- {
- if (is_null($chars)) {
- $chars = static::$_defaultCharPool;
- }
- $charsLen = strlen($chars)-1;
- $str = '';
- for ($i=0; $i<$strLen; $i++) {
- $str .= $chars[mt_rand(0, $charsLen)];
- }
- return $str;
- }
- /**
- * Generate random string based on pattern
- *
- * Syntax: {ULD10}-{U5}
- * - U: upper case letters
- * - L: lower case letters
- * - D: digits
- *
- * @param string $pattern
- * @return string
- */
- public static function randomPattern($pattern)
- {
- static $chars = array('L'=>'bcdfghjkmnpqrstvwxyz', 'U'=>'BCDFGHJKLMNPQRSTVWXYZ', 'D'=>'123456789');
- while (preg_match('#\{([ULD]+)([0-9]+)\}#i', $pattern, $m)) {
- for ($i=0, $c=''; $i<strlen($m[1]); $i++) $c .= $chars[$m[1][$i]];
- $pattern = preg_replace('#'.preg_quote($m[0]).'#', BUtil::randomString($m[2], $c), $pattern, 1);
- }
- return $pattern;
- }
- public static function nextStringValue($string='', $chars=null)
- {
- if (is_null($chars)) {
- $chars = static::$_defaultCharPool; // avoid leading 0
- }
- $pos = strlen($string);
- $lastChar = substr($chars, -1);
- while (--$pos>=-1) {
- if ($pos==-1) {
- $string = $chars[0].$string;
- return $string;
- } elseif ($string[$pos]===$lastChar) {
- $string[$pos] = $chars[0];
- continue;
- } else {
- $string[$pos] = $chars[strpos($chars, $string[$pos])+1];
- return $string;
- }
- }
- // should never get here
- return $string;
- }
- /**
- * Set or retrieve current hash algorithm
- *
- * @param string $algo
- */
- public static function hashAlgo($algo=null)
- {
- if (is_null($algo)) {
- return static::$_hashAlgo;
- }
- static::$_hashAlgo = $algo;
- }
- public static function hashIter($iter=null)
- {
- if (is_null($iter)) {
- return static::$_hashIter;
- }
- static::$iter = $iter;
- }
- /**
- * Generate salted hash
- *
- * @deprecated by Bcrypt
- * @param string $string original text
- * @param mixed $salt
- * @param mixed $algo
- * @return string
- */
- public static function saltedHash($string, $salt, $algo=null)
- {
- $algo = !is_null($algo) ? $algo : static::$_hashAlgo;
- return hash($algo, $salt.$string);
- }
- /**
- * Generate fully composed salted hash
- *
- * Ex: $sha512$2$<salt1>$<salt2>$<double-hashed-string-here>
- *
- * @deprecated by Bcrypt
- * @param string $string
- * @param string $salt
- * @param string $algo
- * @param integer $iter
- */
- public static function fullSaltedHash($string, $salt=null, $algo=null, $iter=null)
- {
- $algo = !is_null($algo) ? $algo : static::$_hashAlgo;
- if ('bcrypt'===$algo) {
- return Bcrypt::i()->hash($string);
- }
- $iter = !is_null($iter) ? $iter : static::$_hashIter;
- $s = static::$_hashSep;
- $hash = $s.$algo.$s.$iter;
- for ($i=0; $i<$iter; $i++) {
- $salt1 = !is_null($salt) ? $salt : static::randomString();
- $hash .= $s.$salt1;
- $string = static::saltedHash($string, $salt1, $algo);
- }
- return $hash.$s.$string;
- }
- /**
- * Validate salted hash against original text
- *
- * @deprecated by BUtil::bcrypt()
- * @param string $string original text
- * @param string $storedHash fully composed salted hash
- * @return bool
- */
- public static function validateSaltedHash($string, $storedHash)
- {
- if (strpos($storedHash, '$2a$')===0 || strpos($storedHash, '$2y$')===0) {
- return Bcrypt::i()->verify($string, $storedHash);
- }
- if (!$storedHash) {
- return false;
- }
- $sep = $storedHash[0];
- $arr = explode($sep, $storedHash);
- array_shift($arr);
- $algo = array_shift($arr);
- $iter = array_shift($arr);
- $verifyHash = $string;
- for ($i=0; $i<$iter; $i++) {
- $salt = array_shift($arr);
- $verifyHash = static::saltedHash($verifyHash, $salt, $algo);
- }
- $knownHash = array_shift($arr);
- return $verifyHash===$knownHash;
- }
- public static function sha512base64($str)
- {
- return base64_encode(pack('H*', hash('sha512', $str)));
- }
- static protected $_lastRemoteHttpInfo;
- /**
- * Send simple POST request to external server and retrieve response
- *
- * @param string $method
- * @param string $url
- * @param array $data
- * @return string
- */
- public static function remoteHttp($method, $url, $data = array())
- {
- $timeout = 5;
- $userAgent = 'Mozilla/5.0';
- if ($method==='GET' && $data) {
- if(is_array($data)){
- $request = http_build_query($data, '', '&');
- } else {
- $request = $data;
- }
- $url .= (strpos($url, '?')===false ? '?' : '&') . $request;
- }
- // curl disabled because file upload doesn't work for some reason. TODO: figure out why
- if (false && function_exists('curl_init')) {
- $curlOpt = array(
- CURLOPT_USERAGENT => $userAgent,
- CURLOPT_URL => $url,
- CURLOPT_ENCODING => '',
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_AUTOREFERER => true,
- CURLOPT_SSL_VERIFYPEER => true,
- CURLOPT_CAINFO => dirname(__DIR__).'/ssl/ca-bundle.crt',
- CURLOPT_SSL_VERIFYHOST => 2,
- CURLOPT_CONNECTTIMEOUT => $timeout,
- CURLOPT_TIMEOUT => $timeout,
- CURLOPT_MAXREDIRS => 10,
- CURLOPT_HTTPHEADER, array('Expect:'), //Fixes the HTTP/1.1 417 Expectation Failed
- CURLOPT_HEADER => true,
- );
- if (false) { // TODO: figure out cookies handling
- $cookieDir = BConfig::i()->get('fs/storage_dir').'/cache';
- BUtil::ensureDir($cookieDir);
- $cookie = tempnam($cookieDir, 'CURLCOOKIE');
- $curlOpt += array(
- CURLOPT_COOKIEJAR => $cookie,
- );
- }
- if ($method==='POST') {
- $curlOpt += array(
- CURLOPT_POSTFIELDS => $data,
- CURLOPT_POST => 1,
- );
- } elseif ($method==='PUT') {
- $curlOpt += array(
- CURLOPT_POSTFIELDS => $data,
- CURLOPT_PUT => 1,
- );
- }
- $ch = curl_init();
- curl_setopt_array($ch, $curlOpt);
- $rawResponse = curl_exec($ch);
- list($response, $headers) = explode("\r\n\r\n", $rawResponse, 2);
- static::$_lastRemoteHttpInfo = curl_getinfo($ch);
- $respHeaders = explode("\r\n", $headers);
- if(curl_errno($ch) != 0){
- static::$_lastRemoteHttpInfo['errno'] = curl_errno($ch);
- static::$_lastRemoteHttpInfo['error'] = curl_error($ch);
- }
- curl_close($ch);
- } else {
- $opts = array('http' => array(
- 'method' => $method,
- 'timeout' => $timeout,
- 'header' => "User-Agent: {$userAgent}\r\n",
- ));
- if ($method==='POST' || $method==='PUT') {
- $multipart = false;
- foreach ($data as $k=>$v) {
- if (is_string($v) && $v[0]==='@') {
- $multipart = true;
- break;
- }
- }
- if (!$multipart) {
- $contentType = 'application/x-www-form-urlencoded';
- $opts['http']['content'] = http_build_query($data);
- } else {
- $boundary = '--------------------------'.microtime(true);
- $contentType = 'multipart/form-data; boundary='.$boundary;
- $opts['http']['content'] = '';
- //TODO: implement recursive forms
- foreach ($data as $k =>$v) {
- if (is_string($v) && $v[0]==='@') {
- $filename = substr($v, 1);
- $fileContents = file_get_contents($filename);
- $opts['http']['content'] .= "--{$boundary}\r\n".
- "Content-Disposition: form-data; name=\"{$k}\"; filename=\"".basename($filename)."\"\r\n".
- "Content-Type: application/zip\r\n".
- "\r\n".
- "{$fileContents}\r\n";
- } else {
- $opts['http']['content'] .= "--{$boundary}\r\n".
- "Content-Disposition: form-data; name=\"{$k}\"\r\n".
- "\r\n".
- "{$v}\r\n";
- }
- }
- $opts['http']['content'] .= "--{$boundary}--\r\n";
- }
- $opts['http']['header'] .= "Content-Type: {$contentType}\r\n";
- //."Content-Length: ".strlen($request)."\r\n";
- if (preg_match('#^(ssl|ftps|https):#', $url)) {
- $opts['ssl'] = array(
- 'verify_peer' => true,
- 'cafile' => dirname(__DIR__).'/ssl/ca-bundle.crt',
- 'verify_depth' => 5,
- );
- }
- }
- $response = file_get_contents($url, false, stream_context_create($opts));
- static::$_lastRemoteHttpInfo = array(); //TODO: emulate curl data?
- $respHeaders = $http_response_header;
- }
- foreach ($respHeaders as $i => $line) {
- if ($i) {
- $arr = explode(':', $line, 2);
- } else {
- $arr = array(0, $line);
- }
- static::$_lastRemoteHttpInfo['headers'][strtolower($arr[0])] = trim($arr[1]);
- }
- return $response;
- }
- public static function lastRemoteHttpInfo()
- {
- return static::$_lastRemoteHttpInfo;
- }
- public static function normalizePath($path)
- {
- $path = str_replace('\\', '/', $path);
- if (strpos($path, '/..')!==false) {
- $a = explode('/', $path);
- $b = array();
- foreach ($a as $p) {
- if ($p==='..') array_pop($b); else $b[] = $p;
- }
- $path = join('/', $b);
- }
- return $path;
- }
- public static function globRecursive($pattern, $flags=0)
- {
- $files = glob($pattern, $flags);
- if (!$files) $files = array();
- $dirs = glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT);
- if ($dirs) {
- foreach ($dirs as $dir) {
- $files = array_merge($files, self::globRecursive($dir.'/'.basename($pattern), $flags));
- }
- }
- return $files;
- }
- public static function isPathAbsolute($path)
- {
- return !empty($path) && ($path[0]==='/' || $path[0]==='\\') // starting with / or \
- || !empty($path[1]) && $path[1]===':'; // windows drive letter C:
- }
- public static function isUrlFull($url)
- {
- return preg_match('#^(https?:)?//#', $url);
- }
- public static function ensureDir($dir)
- {
- if (is_file($dir)) {
- BDebug::warning($dir.' is a file, directory required');
- return;
- }
- if (!is_dir($dir)) {
- @$res = mkdir($dir, 0777, true);
- if (!$res) {
- BDebug::warning("Can't create directory: ".$dir);
- }
- }
- }
- /**
- * Put together URL components generated by parse_url() function
- *
- * @see http://us2.php.net/manual/en/function.parse-url.php#106731
- * @param array $p result of parse_url()
- * @return string
- */
- public static function unparseUrl($p)
- {
- $scheme = isset($p['scheme']) ? $p['scheme'] . '://' : '';
- $user = isset($p['user']) ? $p['user'] : '';
- $pass = isset($p['pass']) ? ':' . $p['pass'] : '';
- $pass = ($user || $pass) ? $pass . '@' : '';
- $host = isset($p['host']) ? $p['host'] : '';
- $port = isset($p['port']) ? ':' . $p['port'] : '';
- $path = isset($p['path']) ? $p['path'] : '';
- $query = isset($p['query']) ? '?' . $p['query'] : '';
- $fragment = isset($p['fragment']) ? '#' . $p['fragment'] : '';
- return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
- }
- /**
- * Add or set URL query parameters
- *
- * @param string $url
- * @param array $params
- * @return string
- */
- public static function setUrlQuery($url, $params)
- {
- if (true === $url) {
- $url = BRequest::currentUrl();
- }
- $parsed = parse_url($url);
- $query = array();
- if (!empty($parsed['query'])) {
- foreach (explode('&', $parsed['query']) as $q) {
- $a = explode('=', $q);
- if ($a[0]==='') {
- continue;
- }
- $a[0] = urldecode($a[0]);
- $query[$a[0]] = urldecode($a[1]);
- }
- }
- foreach($params as $k => $v){
- if($v === ""){
- if(isset($query[$k])){
- unset($query[$k]);
- }
- unset($params[$k]);
- }
- }
- $query = array_merge($query, $params);
- $parsed['query'] = http_build_query($query);
- return static::unparseUrl($parsed);
- }
- public static function paginateSortUrl($url, $state, $field)
- {
- return static::setUrlQuery($url, array(
- 's'=>$field,
- 'sd'=>$state['s']!=$field || $state['sd']=='desc' ? 'asc' : 'desc',
- ));
- }
- public static function paginateSortAttr($url, $state, $field, $class='')
- {
- return 'href="'.static::paginateSortUrl($url, $state, $field)
- .'" class="'.$class.' '.($state['s']==$field ? $state['sd'] : '').'"';
- }
- /**
- * @param string $tag
- * @param array $attrs
- * @param null $content
- * @return string
- */
- public static function tagHtml($tag, $attrs = array(), $content = null)
- {
- $attrsHtmlArr = array();
- foreach ($attrs as $k => $v) {
- if ('' === $v || is_null($v) || false === $v) {
- continue;
- }
- if (true === $v) {
- $v = $k;
- } elseif (is_array($v)) {
- switch ($k) {
- case 'class':
- $v = join(' ', $v);
- break;
- case 'style':
- $attrHtmlArr = array();
- foreach ($v as $k1 => $v1) {
- $attrHtmlArr[] = $k1.':'.$v1;
- }
- $v = join('; ', $attrHtmlArr);
- break;
- default:
- $v = join('', $v);
- }
- }
- $attrsHtmlArr[] = $k.'="'.htmlspecialchars($v, ENT_QUOTES, 'UTF-8').'"';
- }
- return '<'.$tag.' '.join(' ', $attrsHtmlArr).'>'.$content.'</'.$tag.'>';
- }
- /**
- * @param array $options
- * @param string $default
- * @return string
- */
- public static function optionsHtml($options, $default = '')
- {
- if(!is_array($default)){
- $default = (string)$default;
- }
- $htmlArr = array();
- foreach ($options as $k => $v) {
- $k = (string)$k;
- if (is_array($v) && $k!=='' && $k[0] === '@') { // group
- $label = trim(substr($k, 1));
- $htmlArr[] = BUtil::tagHtml('optgroup', array('label' => $label), static::optionsHtml($v, $default));
- continue;
- }
- if (is_array($v)) {
- $attr = $v;
- $v = !empty($attr['text']) ? $attr['text'] : '';
- unset($attr['text']);
- } else {
- $attr = array();
- }
- $attr['value'] = $k;
- $attr['selected'] = is_array($default) && in_array($k, $default) || $default === $k;
- $htmlArr[] = BUtil::tagHtml('option', $attr, $v);
- }
- return join("\n", $htmlArr);
- }
- /**
- * Strip html tags and shorten to specified length, to the whole word
- *
- * @param string $text
- * @param integer $limit
- */
- public static function previewText($text, $limit)
- {
- $text = strip_tags($text);
- if (strlen($text) < $limit) {
- return $text;
- }
- preg_match('/^(.{1,'.$limit.'})\b/', $text, $matches);
- return $matches[1];
- }
- public static function isEmptyDate($date)
- {
- return preg_replace('#[0 :-]#', '', (string)$date)==='';
- }
- /**
- * Get gravatar image src by email
- *
- * @param string $email
- * @param array $params
- * - size (default 80)
- * - rating (G, PG, R, X)
- * - default
- * - border
- */
- public static function gravatar($email, $params=array())
- {
- if (empty($params['default'])) {
- $params['default'] = 'identicon';
- }
- return BRequest::i()->scheme().'://www.gravatar.com/avatar/'.md5(strtolower($email))
- .($params ? '?'.http_build_query($params) : '');
- }
- public static function extCallback($callback)
- {
- if (is_string($callback)) {
- if (strpos($callback, '.')!==false) {
- list($class, $method) = explode('.', $callback);
- } elseif (strpos($callback, '->')) {
- list($class, $method) = explode('->', $callback);
- }
- if (!empty($class)) {
- $callback = array($class::i(), $method);
- }
- }
- return $callback;
- }
- public static function call($callback, $args=array(), $array=false)
- {
- $callback = static::extCallback($callback);
- if ($array) {
- return call_user_func_array($callback, $args);
- } else {
- return call_user_func($callback, $args);
- }
- }
- public static function formatDateRecursive($source, $format='m/d/Y')
- {
- foreach ($source as $i=>$val) {
- if (is_string($val)) {
- // checking only beginning of string for speed, assuming it is a date
- if (preg_match('#^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]( |$)#', $val)) {
- $source[$i] = date($format, strtotime($val));
- }
- } elseif (is_array($val)) {
- $source[$i] = static::formatDateRecursive($val, $format);
- }
- }
- return $source;
- }
- public static function timeAgo($ptime, $now=null, $long=false)
- {
- if (!is_numeric($ptime)) {
- $ptime = strtotime($ptime);
- }
- if (!$now) {
- $now = time();
- } elseif (!is_numeric($now)) {
- $now = strtotime($now);
- }
- $etime = $now - $ptime;
- if ($etime < 1) {
- return $long ? 'less than 1 second' : '0s';
- }
- $a = array(
- 12 * 30 * 24 * 60 * 60 => array('year', 'y'),
- 30 * 24 * 60 * 60 => array('month', 'mon'),
- 24 * 60 * 60 => array('day', 'd'),
- 60 * 60 => array('hour', 'h'),
- 60 => array('minute', 'm'),
- 1 => array('second', 's'),
- );
- foreach ($a as $secs => $sa) {
- $d = $etime / $secs;
- if ($d >= 1) {
- $r = round($d);
- return $r . ($long ? ' ' . $sa[0] . ($r > 1 ? 's' : '') : $sa[1]);
- }
- }
- }
- /**
- * Simplify string to allowed characters only
- *
- * @param string $str input string
- * @param string $pattern RegEx pattern to specify not allowed characters
- * @param string $filler character to replace not allowed characters with
- * @return string
- */
- static public function simplifyString($str, $pattern='#[^a-z0-9-]+#', $filler='-')
- {
- return trim(preg_replace($pattern, $filler, strtolower($str)), $filler);
- }
- /**
- * Remove directory recursively
- *
- * DANGEROUS, I'm afraid to enable it
- *
- * @param string $dir
- */
- /*
- static public function rmdirRecursive_YesIHaveCheckedThreeTimes($dir, $first=true)
- {
- if ($first) {
- $dir = realpath($dir);
- }
- if (!file_exists($dir)) {
- return true;
- }
- if (!is_dir($dir) || is_link($dir)) {
- return unlink($dir);
- }
- foreach (scandir($dir) as $item) {
- if ($item == '.' || $item == '..') {
- continue;
- }
- if (!static::rmdirRecursive($dir . "/" . $item, false)) {
- chmod($dir . "/" . $item, 0777);
- if (!static::rmdirRecursive($dir . "/" . $item, false)) return false;
- }
- }
- return rmdir($dir);
- }
- */
- static public function topoSort(array $array, array $args=array())
- {
- if (empty($array)) {
- return array();
- }
- // nodes listed in 'after' are parents
- // nodes listed in 'before' are children
- // prepare initial $nodes array
- $beforeVar = !empty($args['before']) ? $args['before'] : 'before';
- $afterVar = !empty($args['before']) ? $args['after'] : 'after';
- $isObject = is_object(current($array));
- $nodes = array();
- foreach ($array as $k=>$v) {
- $before = $isObject ? $v->$beforeVar : $v[$beforeVar];
- if (is_string($before)) {
- $before = array_walk(explode(',', $before), 'trim');
- }
- $after = $isObject ? $v->$afterVar : $v[$afterVar];
- if (is_string($after)) {
- $after = array_walk(explode(',', $after), 'trim');
- }
- $nodes[$k] = array('key' => $k, 'item' => $v, 'parents' => (array)$after, 'children' => (array)$before);
- }
- // get nodes without parents
- $rootNodes = array();
- foreach ($nodes as $k=>$node) {
- if (empty($node['parents'])) {
- $rootNodes[] = $node;
- }
- }
- // begin algorithm
- $sorted = array();
- while ($nodes) {
- // check for circular reference
- if (!$rootNodes) return false;
- // remove this node from root nodes and add it to the output
- $n = array_pop($rootNodes);
- $sorted[$n['key']] = $n['item'];
- // for each of its children: queue the new node, finally remove the original
- for ($i = count($n['children'])-1; $i>=0; $i--) {
- // get child node
- $childNode = $nodes[$n['children'][$i]];
- // remove child nodes from parent
- unset($n['children'][$i]);
- // remove parent from child node
- unset($childNode['parents'][array_search($n['name'], $childNode['parents'])]);
- // check if this child has other parents. if not, add it to the root nodes list
- if (!$childNode['parents']) {
- array_push($rootNodes, $childNode);
- }
- }
- // remove processed node from list
- unset($nodes[$n['key']]);
- }
- return $sorted;
- }
- /**
- * Wrapper for ZipArchive::open+extractTo
- *
- * @param string $filename
- * @param string $targetDir
- * @return boolean Result
- */
- static public function zipExtract($filename, $targetDir)
- {
- if (!class_exists('ZipArchive')) {
- throw new BException("Class ZipArchive doesn't exist");
- }
- $zip = new ZipArchive;
- $res = $zip->open($filename);
- if (!$res) {
- throw new BException("Can't open zip archive for reading: " . $filename);
- }
- BUtil::ensureDir($targetDir);
- $res = $zip->extractTo($targetDir);
- $zip->close();
- if (!$res) {
- throw new BException("Can't extract zip archive: " . $filename . " to " . $targetDir);
- }
- return true;
- }
- static public function zipCreateFromDir($filename, $sourceDir)
- {
- if (!class_exists('ZipArchive')) {
- throw new BException("Class ZipArchive doesn't exist");
- }
- $files = BUtil::globRecursive($sourceDir.'/*');
- if (!$files) {
- throw new BException('Invalid or empty source dir');
- }
- $zip = new ZipArchive;
- $res = $zip->open($filename, ZipArchive::CREATE);
- if (!$res) {
- throw new BException("Can't open zip archive for writing: " . $filename);
- }
- foreach ($files as $file) {
- $packedFile = str_replace($sourceDir.'/', '', $file);
- if (is_dir($file)) {
- $zip->addEmptyDir($packedFile);
- } else {
- $zip->addFile($file, $packedFile);
- }
- }
- $zip->close();
- return true;
- }
- }
- class BHTML extends BClass
- {
- }
- /**
- * @todo Verify license compatibility and integrate with https://github.com/PHPMailer/PHPMailer
- */
- class BEmail extends BClass
- {
- static protected $_handlers = array();
- static protected $_defaultHandler = 'default';
- public function __construct()
- {
- $this->addHandler('default', array($this, 'defaultHandler'));
- }
- public function addHandler($name, $params)
- {
- if (is_callable($params)) {
- $params = array(
- 'description' => $name,
- 'callback' => $params,
- );
- }
- static::$_handlers[$name] = $params;
- }
- public function getHandlers()
- {
- return static::$_handlers;
- }
- public function setDefaultHandler($name)
- {
- static::$_defaultHandler = $name;
- }
- public function send($data)
- {
- static $allowedHeadersRegex = '/^(to|from|cc|bcc|reply-to|return-path|content-type|list-unsubscribe|x-.*)$/';
- $data = array_change_key_case($data, CASE_LOWER);
- $body = trim($data['body']);
- unset($data['body']);
- $to = '';
- $subject = '';
- $headers = array();
- $params = array();
- $files = array();
- foreach ($data as $k => $v) {
- if ($k == 'subject') {
- $subject = $v;
- } elseif ($k == 'to') {
- $to = $v;
- } elseif ($k == 'attach') {
- foreach ((array)$v as $file) {
- $files[] = $file;
- }
- } elseif ($k[0] === '-') {
- $params[$k] = $k . ' ' . $v;
- } elseif (preg_match($allowedHeadersRegex, $k)) {
- if (!empty($v) && $v!=='"" <>') {
- $headers[$k] = $k . ': ' . $v;
- }
- }
- }
- $origBody = $body;
- if ($files) {
- // $body and $headers will be updated
- $th…
Large files files are truncated, but you can click here to view the full file