Qiang Xue
12 years ago
16 changed files with 2790 additions and 2633 deletions
@ -0,0 +1,340 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* ArrayHelper provides additional array functionality you can use in your |
||||||
|
* application. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ArrayHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Merges two or more arrays into one recursively. |
||||||
|
* If each array has an element with the same string key value, the latter |
||||||
|
* will overwrite the former (different from array_merge_recursive). |
||||||
|
* Recursive merging will be conducted if both arrays have an element of array |
||||||
|
* type and are having the same key. |
||||||
|
* For integer-keyed elements, the elements from the latter array will |
||||||
|
* be appended to the former array. |
||||||
|
* @param array $a array to be merged to |
||||||
|
* @param array $b array to be merged from. You can specify additional |
||||||
|
* arrays via third argument, fourth argument etc. |
||||||
|
* @return array the merged array (the original arrays are not changed.) |
||||||
|
*/ |
||||||
|
public static function merge($a, $b) |
||||||
|
{ |
||||||
|
$args = func_get_args(); |
||||||
|
$res = array_shift($args); |
||||||
|
while ($args !== array()) { |
||||||
|
$next = array_shift($args); |
||||||
|
foreach ($next as $k => $v) { |
||||||
|
if (is_integer($k)) { |
||||||
|
isset($res[$k]) ? $res[] = $v : $res[$k] = $v; |
||||||
|
} elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) { |
||||||
|
$res[$k] = self::merge($res[$k], $v); |
||||||
|
} else { |
||||||
|
$res[$k] = $v; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return $res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieves the value of an array element or object property with the given key or property name. |
||||||
|
* If the key does not exist in the array, the default value will be returned instead. |
||||||
|
* |
||||||
|
* Below are some usage examples, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* // working with array |
||||||
|
* $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username'); |
||||||
|
* // working with object |
||||||
|
* $username = \yii\helpers\ArrayHelper::getValue($user, 'username'); |
||||||
|
* // working with anonymous function |
||||||
|
* $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) { |
||||||
|
* return $user->firstName . ' ' . $user->lastName; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array|object $array array or object to extract value from |
||||||
|
* @param string|\Closure $key key name of the array element, or property name of the object, |
||||||
|
* or an anonymous function returning the value. The anonymous function signature should be: |
||||||
|
* `function($array, $defaultValue)`. |
||||||
|
* @param mixed $default the default value to be returned if the specified key does not exist |
||||||
|
* @return mixed the value of the |
||||||
|
*/ |
||||||
|
public static function getValue($array, $key, $default = null) |
||||||
|
{ |
||||||
|
if ($key instanceof \Closure) { |
||||||
|
return $key($array, $default); |
||||||
|
} elseif (is_array($array)) { |
||||||
|
return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default; |
||||||
|
} else { |
||||||
|
return $array->$key; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indexes an array according to a specified key. |
||||||
|
* The input array should be multidimensional or an array of objects. |
||||||
|
* |
||||||
|
* The key can be a key name of the sub-array, a property name of object, or an anonymous |
||||||
|
* function which returns the key value given an array element. |
||||||
|
* |
||||||
|
* If a key value is null, the corresponding array element will be discarded and not put in the result. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'data' => 'abc'), |
||||||
|
* array('id' => '345', 'data' => 'def'), |
||||||
|
* ); |
||||||
|
* $result = ArrayHelper::index($array, 'id'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // '123' => array('id' => '123', 'data' => 'abc'), |
||||||
|
* // '345' => array('id' => '345', 'data' => 'def'), |
||||||
|
* // ) |
||||||
|
* |
||||||
|
* // using anonymous function |
||||||
|
* $result = ArrayHelper::index($array, function(element) { |
||||||
|
* return $element['id']; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array the array that needs to be indexed |
||||||
|
* @param string|\Closure $key the column name or anonymous function whose result will be used to index the array |
||||||
|
* @return array the indexed array |
||||||
|
*/ |
||||||
|
public static function index($array, $key) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
foreach ($array as $element) { |
||||||
|
$value = static::getValue($element, $key); |
||||||
|
$result[$value] = $element; |
||||||
|
} |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the values of a specified column in an array. |
||||||
|
* The input array should be multidimensional or an array of objects. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'data' => 'abc'), |
||||||
|
* array('id' => '345', 'data' => 'def'), |
||||||
|
* ); |
||||||
|
* $result = ArrayHelper::getColumn($array, 'id'); |
||||||
|
* // the result is: array( '123', '345') |
||||||
|
* |
||||||
|
* // using anonymous function |
||||||
|
* $result = ArrayHelper::getColumn($array, function(element) { |
||||||
|
* return $element['id']; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array |
||||||
|
* @param string|\Closure $name |
||||||
|
* @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array |
||||||
|
* will be re-indexed with integers. |
||||||
|
* @return array the list of column values |
||||||
|
*/ |
||||||
|
public static function getColumn($array, $name, $keepKeys = true) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
if ($keepKeys) { |
||||||
|
foreach ($array as $k => $element) { |
||||||
|
$result[$k] = static::getValue($element, $name); |
||||||
|
} |
||||||
|
} else { |
||||||
|
foreach ($array as $element) { |
||||||
|
$result[] = static::getValue($element, $name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a map (key-value pairs) from a multidimensional array or an array of objects. |
||||||
|
* The `$from` and `$to` parameters specify the key names or property names to set up the map. |
||||||
|
* Optionally, one can further group the map according to a grouping field `$group`. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'name' => 'aaa', 'class' => 'x'), |
||||||
|
* array('id' => '124', 'name' => 'bbb', 'class' => 'x'), |
||||||
|
* array('id' => '345', 'name' => 'ccc', 'class' => 'y'), |
||||||
|
* ); |
||||||
|
* |
||||||
|
* $result = ArrayHelper::map($array, 'id', 'name'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // '123' => 'aaa', |
||||||
|
* // '124' => 'bbb', |
||||||
|
* // '345' => 'ccc', |
||||||
|
* // ) |
||||||
|
* |
||||||
|
* $result = ArrayHelper::map($array, 'id', 'name', 'class'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // 'x' => array( |
||||||
|
* // '123' => 'aaa', |
||||||
|
* // '124' => 'bbb', |
||||||
|
* // ), |
||||||
|
* // 'y' => array( |
||||||
|
* // '345' => 'ccc', |
||||||
|
* // ), |
||||||
|
* // ) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array |
||||||
|
* @param string|\Closure $from |
||||||
|
* @param string|\Closure $to |
||||||
|
* @param string|\Closure $group |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public static function map($array, $from, $to, $group = null) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
foreach ($array as $element) { |
||||||
|
$key = static::getValue($element, $from); |
||||||
|
$value = static::getValue($element, $to); |
||||||
|
if ($group !== null) { |
||||||
|
$result[static::getValue($element, $group)][$key] = $value; |
||||||
|
} else { |
||||||
|
$result[$key] = $value; |
||||||
|
} |
||||||
|
} |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sorts an array of objects or arrays (with the same structure) by one or several keys. |
||||||
|
* @param array $array the array to be sorted. The array will be modified after calling this method. |
||||||
|
* @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array |
||||||
|
* elements, a property name of the objects, or an anonymous function returning the values for comparison |
||||||
|
* purpose. The anonymous function signature should be: `function($item)`. |
||||||
|
* To sort by multiple keys, provide an array of keys here. |
||||||
|
* @param boolean|array $ascending whether to sort in ascending or descending order. When |
||||||
|
* sorting by multiple keys with different ascending orders, use an array of ascending flags. |
||||||
|
* @param integer|array $sortFlag the PHP sort flag. Valid values include: |
||||||
|
* `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, and `SORT_STRING | SORT_FLAG_CASE`. The last |
||||||
|
* value is for sorting strings in case-insensitive manner. Please refer to |
||||||
|
* See [PHP manual](http://php.net/manual/en/function.sort.php) for more details. |
||||||
|
* When sorting by multiple keys with different sort flags, use an array of sort flags. |
||||||
|
* @throws InvalidParamException if the $ascending or $sortFlag parameters do not have |
||||||
|
* correct number of elements as that of $key. |
||||||
|
*/ |
||||||
|
public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR) |
||||||
|
{ |
||||||
|
$keys = is_array($key) ? $key : array($key); |
||||||
|
if (empty($keys) || empty($array)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
$n = count($keys); |
||||||
|
if (is_scalar($ascending)) { |
||||||
|
$ascending = array_fill(0, $n, $ascending); |
||||||
|
} elseif (count($ascending) !== $n) { |
||||||
|
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||||
|
} |
||||||
|
if (is_scalar($sortFlag)) { |
||||||
|
$sortFlag = array_fill(0, $n, $sortFlag); |
||||||
|
} elseif (count($sortFlag) !== $n) { |
||||||
|
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||||
|
} |
||||||
|
$args = array(); |
||||||
|
foreach ($keys as $i => $key) { |
||||||
|
$flag = $sortFlag[$i]; |
||||||
|
if ($flag == (SORT_STRING | SORT_FLAG_CASE)) { |
||||||
|
$flag = SORT_STRING; |
||||||
|
$column = array(); |
||||||
|
foreach (static::getColumn($array, $key) as $k => $value) { |
||||||
|
$column[$k] = strtolower($value); |
||||||
|
} |
||||||
|
$args[] = $column; |
||||||
|
} else { |
||||||
|
$args[] = static::getColumn($array, $key); |
||||||
|
} |
||||||
|
$args[] = $ascending[$i] ? SORT_ASC : SORT_DESC; |
||||||
|
$args[] = $flag; |
||||||
|
} |
||||||
|
$args[] = &$array; |
||||||
|
call_user_func_array('array_multisort', $args); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Encodes special characters in an array of strings into HTML entities. |
||||||
|
* Both the array keys and values will be encoded. |
||||||
|
* If a value is an array, this method will also encode it recursively. |
||||||
|
* @param array $data data to be encoded |
||||||
|
* @param boolean $valuesOnly whether to encode array values only. If false, |
||||||
|
* both the array keys and array values will be encoded. |
||||||
|
* @param string $charset the charset that the data is using. If not set, |
||||||
|
* [[\yii\base\Application::charset]] will be used. |
||||||
|
* @return array the encoded data |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||||
|
*/ |
||||||
|
public static function htmlEncode($data, $valuesOnly = true, $charset = null) |
||||||
|
{ |
||||||
|
if ($charset === null) { |
||||||
|
$charset = Yii::$app->charset; |
||||||
|
} |
||||||
|
$d = array(); |
||||||
|
foreach ($data as $key => $value) { |
||||||
|
if (!$valuesOnly && is_string($key)) { |
||||||
|
$key = htmlspecialchars($key, ENT_QUOTES, $charset); |
||||||
|
} |
||||||
|
if (is_string($value)) { |
||||||
|
$d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset); |
||||||
|
} elseif (is_array($value)) { |
||||||
|
$d[$key] = static::htmlEncode($value, $charset); |
||||||
|
} |
||||||
|
} |
||||||
|
return $d; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes HTML entities into the corresponding characters in an array of strings. |
||||||
|
* Both the array keys and values will be decoded. |
||||||
|
* If a value is an array, this method will also decode it recursively. |
||||||
|
* @param array $data data to be decoded |
||||||
|
* @param boolean $valuesOnly whether to decode array values only. If false, |
||||||
|
* both the array keys and array values will be decoded. |
||||||
|
* @return array the decoded data |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||||
|
*/ |
||||||
|
public static function htmlDecode($data, $valuesOnly = true) |
||||||
|
{ |
||||||
|
$d = array(); |
||||||
|
foreach ($data as $key => $value) { |
||||||
|
if (!$valuesOnly && is_string($key)) { |
||||||
|
$key = htmlspecialchars_decode($key, ENT_QUOTES); |
||||||
|
} |
||||||
|
if (is_string($value)) { |
||||||
|
$d[$key] = htmlspecialchars_decode($value, ENT_QUOTES); |
||||||
|
} elseif (is_array($value)) { |
||||||
|
$d[$key] = static::htmlDecode($value); |
||||||
|
} |
||||||
|
} |
||||||
|
return $d; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,470 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
// todo test this on all kinds of terminals, especially windows (check out lib ncurses) |
||||||
|
|
||||||
|
/** |
||||||
|
* Console View is the base class for console view components |
||||||
|
* |
||||||
|
* A console view provides functionality to create rich console application by allowing to format output |
||||||
|
* by adding color and font style to it. |
||||||
|
* |
||||||
|
* @author Carsten Brandt <mail@cebe.cc> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ConsoleColor |
||||||
|
{ |
||||||
|
const FG_BLACK = 30; |
||||||
|
const FG_RED = 31; |
||||||
|
const FG_GREEN = 32; |
||||||
|
const FG_YELLOW = 33; |
||||||
|
const FG_BLUE = 34; |
||||||
|
const FG_PURPLE = 35; |
||||||
|
const FG_CYAN = 36; |
||||||
|
const FG_GREY = 37; |
||||||
|
|
||||||
|
const BG_BLACK = 40; |
||||||
|
const BG_RED = 41; |
||||||
|
const BG_GREEN = 42; |
||||||
|
const BG_YELLOW = 43; |
||||||
|
const BG_BLUE = 44; |
||||||
|
const BG_PURPLE = 45; |
||||||
|
const BG_CYAN = 46; |
||||||
|
const BG_GREY = 47; |
||||||
|
|
||||||
|
const BOLD = 1; |
||||||
|
const ITALIC = 3; |
||||||
|
const UNDERLINE = 4; |
||||||
|
const BLINK = 5; |
||||||
|
const NEGATIVE = 7; |
||||||
|
const CONCEALED = 8; |
||||||
|
const CROSSED_OUT = 9; |
||||||
|
const FRAMED = 51; |
||||||
|
const ENCIRCLED = 52; |
||||||
|
const OVERLINED = 53; |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor up by sending ANSI control code CUU to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $rows number of rows the cursor should be moved up |
||||||
|
*/ |
||||||
|
public static function moveCursorUp($rows=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $rows . 'A'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor down by sending ANSI control code CUD to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $rows number of rows the cursor should be moved down |
||||||
|
*/ |
||||||
|
public static function moveCursorDown($rows=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $rows . 'B'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor forward by sending ANSI control code CUF to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $steps number of steps the cursor should be moved forward |
||||||
|
*/ |
||||||
|
public static function moveCursorForward($steps=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $steps . 'C'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor backward by sending ANSI control code CUB to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $steps number of steps the cursor should be moved backward |
||||||
|
*/ |
||||||
|
public static function moveCursorBackward($steps=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $steps . 'D'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal. |
||||||
|
* @param integer $lines number of lines the cursor should be moved down |
||||||
|
*/ |
||||||
|
public static function moveCursorNextLine($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $lines . 'E'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal. |
||||||
|
* @param integer $lines number of lines the cursor should be moved up |
||||||
|
*/ |
||||||
|
public static function moveCursorPrevLine($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $lines . 'F'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal. |
||||||
|
* @param integer $column 1-based column number, 1 is the left edge of the screen. |
||||||
|
* @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line. |
||||||
|
*/ |
||||||
|
public static function moveCursorTo($column, $row=null) |
||||||
|
{ |
||||||
|
if ($row === null) { |
||||||
|
echo "\033[" . (int) $column . 'G'; |
||||||
|
} else { |
||||||
|
echo "\033[" . (int) $row . ';' . (int) $column . 'H'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Scrolls whole page up by sending ANSI control code SU to the terminal. |
||||||
|
* New lines are added at the bottom. This is not supported by ANSI.SYS used in windows. |
||||||
|
* @param int $lines number of lines to scroll up |
||||||
|
*/ |
||||||
|
public static function scrollUp($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[".(int)$lines."S"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Scrolls whole page down by sending ANSI control code SD to the terminal. |
||||||
|
* New lines are added at the top. This is not supported by ANSI.SYS used in windows. |
||||||
|
* @param int $lines number of lines to scroll down |
||||||
|
*/ |
||||||
|
public static function scrollDown($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[".(int)$lines."T"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Saves the current cursor position by sending ANSI control code SCP to the terminal. |
||||||
|
* Position can then be restored with {@link restoreCursorPosition}. |
||||||
|
*/ |
||||||
|
public static function saveCursorPosition() |
||||||
|
{ |
||||||
|
echo "\033[s"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal. |
||||||
|
*/ |
||||||
|
public static function restoreCursorPosition() |
||||||
|
{ |
||||||
|
echo "\033[u"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal. |
||||||
|
* Use {@link showCursor} to bring it back. |
||||||
|
* Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit. |
||||||
|
*/ |
||||||
|
public static function hideCursor() |
||||||
|
{ |
||||||
|
echo "\033[?25l"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal. |
||||||
|
*/ |
||||||
|
public static function showCursor() |
||||||
|
{ |
||||||
|
echo "\033[?25h"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
* **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen. |
||||||
|
*/ |
||||||
|
public static function clearScreen() |
||||||
|
{ |
||||||
|
echo "\033[2J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearScreenBeforeCursor() |
||||||
|
{ |
||||||
|
echo "\033[1J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearScreenAfterCursor() |
||||||
|
{ |
||||||
|
echo "\033[0J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLine() |
||||||
|
{ |
||||||
|
echo "\033[2K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLineBeforeCursor() |
||||||
|
{ |
||||||
|
echo "\033[1K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLineAfterCursor() |
||||||
|
{ |
||||||
|
echo "\033[0K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will send ANSI format for following output |
||||||
|
* |
||||||
|
* You can pass any of the FG_*, BG_* and TEXT_* constants and also xterm256ColorBg |
||||||
|
* TODO: documentation |
||||||
|
*/ |
||||||
|
public static function ansiStyle() |
||||||
|
{ |
||||||
|
echo "\033[" . implode(';', func_get_args()) . 'm'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will return a string formatted with the given ANSI style |
||||||
|
* |
||||||
|
* See {@link ansiStyle} for possible arguments. |
||||||
|
* @param string $string the string to be formatted |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function ansiStyleString($string) |
||||||
|
{ |
||||||
|
$args = func_get_args(); |
||||||
|
array_shift($args); |
||||||
|
$code = implode(';', $args); |
||||||
|
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string."\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||||||
|
public static function xterm256ColorFg($i) // TODO naming! |
||||||
|
{ |
||||||
|
return '38;5;'.$i; |
||||||
|
} |
||||||
|
|
||||||
|
public static function xterm256ColorBg($i) // TODO naming! |
||||||
|
{ |
||||||
|
return '48;5;'.$i; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Usage: list($w, $h) = ConsoleHelper::getScreenSize(); |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public static function getScreenSize() |
||||||
|
{ |
||||||
|
// TODO implement |
||||||
|
return array(150,50); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* resets any ansi style set by previous method {@link ansiStyle} |
||||||
|
* Any output after this is will have default text style. |
||||||
|
*/ |
||||||
|
public static function reset() |
||||||
|
{ |
||||||
|
echo "\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Strips ANSI control codes from a string |
||||||
|
* |
||||||
|
* @param string $string String to strip |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function strip($string) |
||||||
|
{ |
||||||
|
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color |
||||||
|
} |
||||||
|
|
||||||
|
// TODO refactor and review |
||||||
|
public static function ansiToHtml($string) |
||||||
|
{ |
||||||
|
$tags = 0; |
||||||
|
return preg_replace_callback('/\033\[[\d;]+m/', function($ansi) use (&$tags) { |
||||||
|
$styleA = array(); |
||||||
|
foreach(explode(';', $ansi) as $controlCode) |
||||||
|
{ |
||||||
|
switch($controlCode) |
||||||
|
{ |
||||||
|
case static::FG_BLACK: $style = array('color' => '#000000'); break; |
||||||
|
case static::FG_BLUE: $style = array('color' => '#000078'); break; |
||||||
|
case static::FG_CYAN: $style = array('color' => '#007878'); break; |
||||||
|
case static::FG_GREEN: $style = array('color' => '#007800'); break; |
||||||
|
case static::FG_GREY: $style = array('color' => '#787878'); break; |
||||||
|
case static::FG_PURPLE: $style = array('color' => '#780078'); break; |
||||||
|
case static::FG_RED: $style = array('color' => '#780000'); break; |
||||||
|
case static::FG_YELLOW: $style = array('color' => '#787800'); break; |
||||||
|
case static::BG_BLACK: $style = array('background-color' => '#000000'); break; |
||||||
|
case static::BG_BLUE: $style = array('background-color' => '#000078'); break; |
||||||
|
case static::BG_CYAN: $style = array('background-color' => '#007878'); break; |
||||||
|
case static::BG_GREEN: $style = array('background-color' => '#007800'); break; |
||||||
|
case static::BG_GREY: $style = array('background-color' => '#787878'); break; |
||||||
|
case static::BG_PURPLE: $style = array('background-color' => '#780078'); break; |
||||||
|
case static::BG_RED: $style = array('background-color' => '#780000'); break; |
||||||
|
case static::BG_YELLOW: $style = array('background-color' => '#787800'); break; |
||||||
|
case static::BOLD: $style = array('font-weight' => 'bold'); break; |
||||||
|
case static::ITALIC: $style = array('font-style' => 'italic'); break; |
||||||
|
case static::UNDERLINE: $style = array('text-decoration' => array('underline')); break; |
||||||
|
case static::OVERLINED: $style = array('text-decoration' => array('overline')); break; |
||||||
|
case static::CROSSED_OUT:$style = array('text-decoration' => array('line-through')); break; |
||||||
|
case static::BLINK: $style = array('text-decoration' => array('blink')); break; |
||||||
|
case static::NEGATIVE: // ??? |
||||||
|
case static::CONCEALED: |
||||||
|
case static::ENCIRCLED: |
||||||
|
case static::FRAMED: |
||||||
|
// TODO allow resetting codes |
||||||
|
break; |
||||||
|
case 0: // ansi reset |
||||||
|
$return = ''; |
||||||
|
for($n=$tags; $tags>0; $tags--) { |
||||||
|
$return .= '</span>'; |
||||||
|
} |
||||||
|
return $return; |
||||||
|
} |
||||||
|
|
||||||
|
$styleA = ArrayHelper::merge($styleA, $style); |
||||||
|
} |
||||||
|
$styleString[] = array(); |
||||||
|
foreach($styleA as $name => $content) { |
||||||
|
if ($name === 'text-decoration') { |
||||||
|
$content = implode(' ', $content); |
||||||
|
} |
||||||
|
$styleString[] = $name.':'.$content; |
||||||
|
} |
||||||
|
$tags++; |
||||||
|
return '<span' . (!empty($styleString) ? 'style="' . implode(';', $styleString) : '') . '>'; |
||||||
|
}, $string); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php |
||||||
|
* |
||||||
|
* Converts colorcodes in the format %y (for yellow) into ansi-control |
||||||
|
* codes. The conversion table is: ('bold' meaning 'light' on some |
||||||
|
* terminals). It's almost the same conversion table irssi uses. |
||||||
|
* <pre> |
||||||
|
* text text background |
||||||
|
* ------------------------------------------------ |
||||||
|
* %k %K %0 black dark grey black |
||||||
|
* %r %R %1 red bold red red |
||||||
|
* %g %G %2 green bold green green |
||||||
|
* %y %Y %3 yellow bold yellow yellow |
||||||
|
* %b %B %4 blue bold blue blue |
||||||
|
* %m %M %5 magenta bold magenta magenta |
||||||
|
* %p %P magenta (think: purple) |
||||||
|
* %c %C %6 cyan bold cyan cyan |
||||||
|
* %w %W %7 white bold white white |
||||||
|
* |
||||||
|
* %F Blinking, Flashing |
||||||
|
* %U Underline |
||||||
|
* %8 Reverse |
||||||
|
* %_,%9 Bold |
||||||
|
* |
||||||
|
* %n Resets the color |
||||||
|
* %% A single % |
||||||
|
* </pre> |
||||||
|
* First param is the string to convert, second is an optional flag if |
||||||
|
* colors should be used. It defaults to true, if set to false, the |
||||||
|
* colorcodes will just be removed (And %% will be transformed into %) |
||||||
|
* |
||||||
|
* @param string $string String to convert |
||||||
|
* @param bool $colored Should the string be colored? |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function renderColoredString($string) |
||||||
|
{ |
||||||
|
$colored = true; |
||||||
|
|
||||||
|
|
||||||
|
static $conversions = array ( // static so the array doesn't get built |
||||||
|
// everytime |
||||||
|
// %y - yellow, and so on... {{{ |
||||||
|
'%y' => array('color' => 'yellow'), |
||||||
|
'%g' => array('color' => 'green' ), |
||||||
|
'%b' => array('color' => 'blue' ), |
||||||
|
'%r' => array('color' => 'red' ), |
||||||
|
'%p' => array('color' => 'purple'), |
||||||
|
'%m' => array('color' => 'purple'), |
||||||
|
'%c' => array('color' => 'cyan' ), |
||||||
|
'%w' => array('color' => 'grey' ), |
||||||
|
'%k' => array('color' => 'black' ), |
||||||
|
'%n' => array('color' => 'reset' ), |
||||||
|
'%Y' => array('color' => 'yellow', 'style' => 'light'), |
||||||
|
'%G' => array('color' => 'green', 'style' => 'light'), |
||||||
|
'%B' => array('color' => 'blue', 'style' => 'light'), |
||||||
|
'%R' => array('color' => 'red', 'style' => 'light'), |
||||||
|
'%P' => array('color' => 'purple', 'style' => 'light'), |
||||||
|
'%M' => array('color' => 'purple', 'style' => 'light'), |
||||||
|
'%C' => array('color' => 'cyan', 'style' => 'light'), |
||||||
|
'%W' => array('color' => 'grey', 'style' => 'light'), |
||||||
|
'%K' => array('color' => 'black', 'style' => 'light'), |
||||||
|
'%N' => array('color' => 'reset', 'style' => 'light'), |
||||||
|
'%3' => array('background' => 'yellow'), |
||||||
|
'%2' => array('background' => 'green' ), |
||||||
|
'%4' => array('background' => 'blue' ), |
||||||
|
'%1' => array('background' => 'red' ), |
||||||
|
'%5' => array('background' => 'purple'), |
||||||
|
'%6' => array('background' => 'cyan' ), |
||||||
|
'%7' => array('background' => 'grey' ), |
||||||
|
'%0' => array('background' => 'black' ), |
||||||
|
// Don't use this, I can't stand flashing text |
||||||
|
'%F' => array('style' => 'blink'), |
||||||
|
'%U' => array('style' => 'underline'), |
||||||
|
'%8' => array('style' => 'inverse'), |
||||||
|
'%9' => array('style' => 'bold'), |
||||||
|
'%_' => array('style' => 'bold') |
||||||
|
// }}} |
||||||
|
); |
||||||
|
|
||||||
|
if ($colored) { |
||||||
|
$string = str_replace('%%', '% ', $string); |
||||||
|
foreach ($conversions as $key => $value) { |
||||||
|
$string = str_replace($key, Console_Color::color($value), |
||||||
|
$string); |
||||||
|
} |
||||||
|
$string = str_replace('% ', '%', $string); |
||||||
|
|
||||||
|
} else { |
||||||
|
$string = preg_replace('/%((%)|.)/', '$2', $string); |
||||||
|
} |
||||||
|
|
||||||
|
return $string; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Escapes % so they don't get interpreted as color codes |
||||||
|
* |
||||||
|
* @param string $string String to escape |
||||||
|
* |
||||||
|
* @access public |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function escape($string) |
||||||
|
{ |
||||||
|
return str_replace('%', '%%', $string); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,274 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Filesystem helper class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use yii\base\Exception; |
||||||
|
use yii\base\InvalidConfigException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Filesystem helper |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Alex Makarov <sam@rmcreative.ru> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class FileHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Returns the extension name of a file path. |
||||||
|
* For example, the path "path/to/something.php" would return "php". |
||||||
|
* @param string $path the file path |
||||||
|
* @return string the extension name without the dot character. |
||||||
|
*/ |
||||||
|
public static function getExtension($path) |
||||||
|
{ |
||||||
|
return pathinfo($path, PATHINFO_EXTENSION); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks the given path and ensures it is a directory. |
||||||
|
* This method will call `realpath()` to "normalize" the given path. |
||||||
|
* If the given path does not refer to an existing directory, an exception will be thrown. |
||||||
|
* @param string $path the given path. This can also be a path alias. |
||||||
|
* @return string the normalized path |
||||||
|
* @throws InvalidConfigException if the path does not refer to an existing directory. |
||||||
|
*/ |
||||||
|
public static function ensureDirectory($path) |
||||||
|
{ |
||||||
|
$p = \Yii::getAlias($path); |
||||||
|
if (($p = realpath($p)) !== false && is_dir($p)) { |
||||||
|
return $p; |
||||||
|
} else { |
||||||
|
throw new InvalidConfigException('Directory does not exist: ' . $path); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Normalizes a file/directory path. |
||||||
|
* After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`, |
||||||
|
* and any trailing directory separators will be removed. For example, '/home\demo/' on Linux |
||||||
|
* will be normalized as '/home/demo'. |
||||||
|
* @param string $path the file/directory path to be normalized |
||||||
|
* @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`. |
||||||
|
* @return string the normalized file/directory path |
||||||
|
*/ |
||||||
|
public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR) |
||||||
|
{ |
||||||
|
return rtrim(strtr($path, array('/' => $ds, '\\' => $ds)), $ds); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the localized version of a specified file. |
||||||
|
* |
||||||
|
* The searching is based on the specified language code. In particular, |
||||||
|
* a file with the same name will be looked for under the subdirectory |
||||||
|
* whose name is same as the language code. For example, given the file "path/to/view.php" |
||||||
|
* and language code "zh_cn", the localized file will be looked for as |
||||||
|
* "path/to/zh_cn/view.php". If the file is not found, the original file |
||||||
|
* will be returned. |
||||||
|
* |
||||||
|
* If the target and the source language codes are the same, |
||||||
|
* the original file will be returned. |
||||||
|
* |
||||||
|
* For consistency, it is recommended that the language code is given |
||||||
|
* in lower case and in the format of LanguageID_RegionID (e.g. "en_us"). |
||||||
|
* |
||||||
|
* @param string $file the original file |
||||||
|
* @param string $language the target language that the file should be localized to. |
||||||
|
* If not set, the value of [[\yii\base\Application::language]] will be used. |
||||||
|
* @param string $sourceLanguage the language that the original file is in. |
||||||
|
* If not set, the value of [[\yii\base\Application::sourceLanguage]] will be used. |
||||||
|
* @return string the matching localized file, or the original file if the localized version is not found. |
||||||
|
* If the target and the source language codes are the same, the original file will be returned. |
||||||
|
*/ |
||||||
|
public static function localize($file, $language = null, $sourceLanguage = null) |
||||||
|
{ |
||||||
|
if ($language === null) { |
||||||
|
$language = \Yii::$app->language; |
||||||
|
} |
||||||
|
if ($sourceLanguage === null) { |
||||||
|
$sourceLanguage = \Yii::$app->sourceLanguage; |
||||||
|
} |
||||||
|
if ($language === $sourceLanguage) { |
||||||
|
return $file; |
||||||
|
} |
||||||
|
$desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . basename($file); |
||||||
|
return is_file($desiredFile) ? $desiredFile : $file; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines the MIME type of the specified file. |
||||||
|
* This method will first try to determine the MIME type based on |
||||||
|
* [finfo_open](http://php.net/manual/en/function.finfo-open.php). If this doesn't work, it will |
||||||
|
* fall back to [[getMimeTypeByExtension()]]. |
||||||
|
* @param string $file the file name. |
||||||
|
* @param string $magicFile name of the optional magic database file, usually something like `/path/to/magic.mime`. |
||||||
|
* This will be passed as the second parameter to [finfo_open](http://php.net/manual/en/function.finfo-open.php). |
||||||
|
* @param boolean $checkExtension whether to use the file extension to determine the MIME type in case |
||||||
|
* `finfo_open()` cannot determine it. |
||||||
|
* @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined. |
||||||
|
*/ |
||||||
|
public static function getMimeType($file, $magicFile = null, $checkExtension = true) |
||||||
|
{ |
||||||
|
if (function_exists('finfo_open')) { |
||||||
|
$info = finfo_open(FILEINFO_MIME_TYPE, $magicFile); |
||||||
|
if ($info && ($result = finfo_file($info, $file)) !== false) { |
||||||
|
return $result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $checkExtension ? self::getMimeTypeByExtension($file) : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines the MIME type based on the extension name of the specified file. |
||||||
|
* This method will use a local map between extension names and MIME types. |
||||||
|
* @param string $file the file name. |
||||||
|
* @param string $magicFile the path of the file that contains all available MIME type information. |
||||||
|
* If this is not set, the default file aliased by `@yii/util/mimeTypes.php` will be used. |
||||||
|
* @return string the MIME type. Null is returned if the MIME type cannot be determined. |
||||||
|
*/ |
||||||
|
public static function getMimeTypeByExtension($file, $magicFile = null) |
||||||
|
{ |
||||||
|
if ($magicFile === null) { |
||||||
|
$magicFile = \Yii::getAlias('@yii/util/mimeTypes.php'); |
||||||
|
} |
||||||
|
$mimeTypes = require($magicFile); |
||||||
|
if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { |
||||||
|
$ext = strtolower($ext); |
||||||
|
if (isset($mimeTypes[$ext])) { |
||||||
|
return $mimeTypes[$ext]; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Copies a list of files from one place to another. |
||||||
|
* @param array $fileList the list of files to be copied (name=>spec). |
||||||
|
* The array keys are names displayed during the copy process, and array values are specifications |
||||||
|
* for files to be copied. Each array value must be an array of the following structure: |
||||||
|
* <ul> |
||||||
|
* <li>source: required, the full path of the file/directory to be copied from</li> |
||||||
|
* <li>target: required, the full path of the file/directory to be copied to</li> |
||||||
|
* <li>callback: optional, the callback to be invoked when copying a file. The callback function |
||||||
|
* should be declared as follows: |
||||||
|
* <pre> |
||||||
|
* function foo($source,$params) |
||||||
|
* </pre> |
||||||
|
* where $source parameter is the source file path, and the content returned |
||||||
|
* by the function will be saved into the target file.</li> |
||||||
|
* <li>params: optional, the parameters to be passed to the callback</li> |
||||||
|
* </ul> |
||||||
|
* @see buildFileList |
||||||
|
*/ |
||||||
|
public static function copyFiles($fileList) |
||||||
|
{ |
||||||
|
$overwriteAll = false; |
||||||
|
foreach($fileList as $name=>$file) { |
||||||
|
$source = strtr($file['source'], '/\\', DIRECTORY_SEPARATOR); |
||||||
|
$target = strtr($file['target'], '/\\', DIRECTORY_SEPARATOR); |
||||||
|
$callback = isset($file['callback']) ? $file['callback'] : null; |
||||||
|
$params = isset($file['params']) ? $file['params'] : null; |
||||||
|
|
||||||
|
if(is_dir($source)) { |
||||||
|
try { |
||||||
|
self::ensureDirectory($target); |
||||||
|
} |
||||||
|
catch (Exception $e) { |
||||||
|
mkdir($target, true, 0777); |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if($callback !== null) { |
||||||
|
$content = call_user_func($callback, $source, $params); |
||||||
|
} |
||||||
|
else { |
||||||
|
$content = file_get_contents($source); |
||||||
|
} |
||||||
|
if(is_file($target)) { |
||||||
|
if($content === file_get_contents($target)) { |
||||||
|
echo " unchanged $name\n"; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if($overwriteAll) { |
||||||
|
echo " overwrite $name\n"; |
||||||
|
} |
||||||
|
else { |
||||||
|
echo " exist $name\n"; |
||||||
|
echo " ...overwrite? [Yes|No|All|Quit] "; |
||||||
|
$answer = trim(fgets(STDIN)); |
||||||
|
if(!strncasecmp($answer, 'q', 1)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
elseif(!strncasecmp($answer, 'y', 1)) { |
||||||
|
echo " overwrite $name\n"; |
||||||
|
} |
||||||
|
elseif(!strncasecmp($answer, 'a', 1)) { |
||||||
|
echo " overwrite $name\n"; |
||||||
|
$overwriteAll = true; |
||||||
|
} |
||||||
|
else { |
||||||
|
echo " skip $name\n"; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
try { |
||||||
|
self::ensureDirectory(dirname($target)); |
||||||
|
} |
||||||
|
catch (Exception $e) { |
||||||
|
mkdir(dirname($target), true, 0777); |
||||||
|
} |
||||||
|
echo " generate $name\n"; |
||||||
|
} |
||||||
|
file_put_contents($target, $content); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds the file list of a directory. |
||||||
|
* This method traverses through the specified directory and builds |
||||||
|
* a list of files and subdirectories that the directory contains. |
||||||
|
* The result of this function can be passed to {@link copyFiles}. |
||||||
|
* @param string $sourceDir the source directory |
||||||
|
* @param string $targetDir the target directory |
||||||
|
* @param string $baseDir base directory |
||||||
|
* @param array $ignoreFiles list of the names of files that should |
||||||
|
* be ignored in list building process. Argument available since 1.1.11. |
||||||
|
* @param array $renameMap hash array of file names that should be |
||||||
|
* renamed. Example value: array('1.old.txt'=>'2.new.txt'). |
||||||
|
* @return array the file list (see {@link copyFiles}) |
||||||
|
*/ |
||||||
|
public static function buildFileList($sourceDir, $targetDir, $baseDir='', $ignoreFiles=array(), $renameMap=array()) |
||||||
|
{ |
||||||
|
$list = array(); |
||||||
|
$handle = opendir($sourceDir); |
||||||
|
while(($file = readdir($handle)) !== false) { |
||||||
|
if(in_array($file, array('.', '..', '.svn', '.gitignore')) || in_array($file, $ignoreFiles)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$sourcePath = $sourceDir.DIRECTORY_SEPARATOR.$file; |
||||||
|
$targetPath = $targetDir.DIRECTORY_SEPARATOR.strtr($file, $renameMap); |
||||||
|
$name = $baseDir === '' ? $file : $baseDir.'/'.$file; |
||||||
|
$list[$name] = array( |
||||||
|
'source' => $sourcePath, |
||||||
|
'target' => $targetPath, |
||||||
|
); |
||||||
|
if(is_dir($sourcePath)) { |
||||||
|
$list = array_merge($list, self::buildFileList($sourcePath, $targetPath, $name, $ignoreFiles, $renameMap)); |
||||||
|
} |
||||||
|
} |
||||||
|
closedir($handle); |
||||||
|
return $list; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,981 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Html provides a set of static methods for generating commonly used HTML tags. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Html |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var boolean whether to close void (empty) elements. Defaults to true. |
||||||
|
* @see voidElements |
||||||
|
*/ |
||||||
|
public static $closeVoidElements = true; |
||||||
|
/** |
||||||
|
* @var array list of void elements (element name => 1) |
||||||
|
* @see closeVoidElements |
||||||
|
* @see http://www.w3.org/TR/html-markup/syntax.html#void-element |
||||||
|
*/ |
||||||
|
public static $voidElements = array( |
||||||
|
'area' => 1, |
||||||
|
'base' => 1, |
||||||
|
'br' => 1, |
||||||
|
'col' => 1, |
||||||
|
'command' => 1, |
||||||
|
'embed' => 1, |
||||||
|
'hr' => 1, |
||||||
|
'img' => 1, |
||||||
|
'input' => 1, |
||||||
|
'keygen' => 1, |
||||||
|
'link' => 1, |
||||||
|
'meta' => 1, |
||||||
|
'param' => 1, |
||||||
|
'source' => 1, |
||||||
|
'track' => 1, |
||||||
|
'wbr' => 1, |
||||||
|
); |
||||||
|
/** |
||||||
|
* @var boolean whether to show the values of boolean attributes in element tags. |
||||||
|
* If false, only the attribute names will be generated. |
||||||
|
* @see booleanAttributes |
||||||
|
*/ |
||||||
|
public static $showBooleanAttributeValues = true; |
||||||
|
/** |
||||||
|
* @var array list of boolean attributes. The presence of a boolean attribute on |
||||||
|
* an element represents the true value, and the absence of the attribute represents the false value. |
||||||
|
* @see showBooleanAttributeValues |
||||||
|
* @see http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes |
||||||
|
*/ |
||||||
|
public static $booleanAttributes = array( |
||||||
|
'async' => 1, |
||||||
|
'autofocus' => 1, |
||||||
|
'autoplay' => 1, |
||||||
|
'checked' => 1, |
||||||
|
'controls' => 1, |
||||||
|
'declare' => 1, |
||||||
|
'default' => 1, |
||||||
|
'defer' => 1, |
||||||
|
'disabled' => 1, |
||||||
|
'formnovalidate' => 1, |
||||||
|
'hidden' => 1, |
||||||
|
'ismap' => 1, |
||||||
|
'loop' => 1, |
||||||
|
'multiple' => 1, |
||||||
|
'muted' => 1, |
||||||
|
'nohref' => 1, |
||||||
|
'noresize' => 1, |
||||||
|
'novalidate' => 1, |
||||||
|
'open' => 1, |
||||||
|
'readonly' => 1, |
||||||
|
'required' => 1, |
||||||
|
'reversed' => 1, |
||||||
|
'scoped' => 1, |
||||||
|
'seamless' => 1, |
||||||
|
'selected' => 1, |
||||||
|
'typemustmatch' => 1, |
||||||
|
); |
||||||
|
/** |
||||||
|
* @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes |
||||||
|
* that are rendered by [[renderAttributes()]]. |
||||||
|
*/ |
||||||
|
public static $attributeOrder = array( |
||||||
|
'type', |
||||||
|
'id', |
||||||
|
'class', |
||||||
|
'name', |
||||||
|
'value', |
||||||
|
|
||||||
|
'href', |
||||||
|
'src', |
||||||
|
'action', |
||||||
|
'method', |
||||||
|
|
||||||
|
'selected', |
||||||
|
'checked', |
||||||
|
'readonly', |
||||||
|
'disabled', |
||||||
|
'multiple', |
||||||
|
|
||||||
|
'size', |
||||||
|
'maxlength', |
||||||
|
'width', |
||||||
|
'height', |
||||||
|
'rows', |
||||||
|
'cols', |
||||||
|
|
||||||
|
'alt', |
||||||
|
'title', |
||||||
|
'rel', |
||||||
|
'media', |
||||||
|
); |
||||||
|
|
||||||
|
/** |
||||||
|
* Encodes special characters into HTML entities. |
||||||
|
* The [[yii\base\Application::charset|application charset]] will be used for encoding. |
||||||
|
* @param string $content the content to be encoded |
||||||
|
* @return string the encoded content |
||||||
|
* @see decode |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||||
|
*/ |
||||||
|
public static function encode($content) |
||||||
|
{ |
||||||
|
return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes special HTML entities back to the corresponding characters. |
||||||
|
* This is the opposite of [[encode()]]. |
||||||
|
* @param string $content the content to be decoded |
||||||
|
* @return string the decoded content |
||||||
|
* @see encode |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||||
|
*/ |
||||||
|
public static function decode($content) |
||||||
|
{ |
||||||
|
return htmlspecialchars_decode($content, ENT_QUOTES); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a complete HTML tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded. |
||||||
|
* If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated HTML tag |
||||||
|
* @see beginTag |
||||||
|
* @see endTag |
||||||
|
*/ |
||||||
|
public static function tag($name, $content = '', $options = array()) |
||||||
|
{ |
||||||
|
$html = '<' . $name . static::renderTagAttributes($options); |
||||||
|
if (isset(static::$voidElements[strtolower($name)])) { |
||||||
|
return $html . (static::$closeVoidElements ? ' />' : '>'); |
||||||
|
} else { |
||||||
|
return $html . ">$content</$name>"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a start tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated start tag |
||||||
|
* @see endTag |
||||||
|
* @see tag |
||||||
|
*/ |
||||||
|
public static function beginTag($name, $options = array()) |
||||||
|
{ |
||||||
|
return '<' . $name . static::renderTagAttributes($options) . '>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an end tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @return string the generated end tag |
||||||
|
* @see beginTag |
||||||
|
* @see tag |
||||||
|
*/ |
||||||
|
public static function endTag($name) |
||||||
|
{ |
||||||
|
return "</$name>"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Encloses the given content within a CDATA tag. |
||||||
|
* @param string $content the content to be enclosed within the CDATA tag |
||||||
|
* @return string the CDATA tag with the enclosed content. |
||||||
|
*/ |
||||||
|
public static function cdata($content) |
||||||
|
{ |
||||||
|
return '<![CDATA[' . $content . ']]>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a style tag. |
||||||
|
* @param string $content the style content |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "text/css" will be used. |
||||||
|
* @return string the generated style tag |
||||||
|
*/ |
||||||
|
public static function style($content, $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'text/css'; |
||||||
|
} |
||||||
|
return static::tag('style', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a script tag. |
||||||
|
* @param string $content the script content |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered. |
||||||
|
* @return string the generated script tag |
||||||
|
*/ |
||||||
|
public static function script($content, $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'text/javascript'; |
||||||
|
} |
||||||
|
return static::tag('script', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a link tag that refers to an external CSS file. |
||||||
|
* @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated link tag |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function cssFile($url, $options = array()) |
||||||
|
{ |
||||||
|
$options['rel'] = 'stylesheet'; |
||||||
|
$options['type'] = 'text/css'; |
||||||
|
$options['href'] = static::url($url); |
||||||
|
return static::tag('link', '', $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a script tag that refers to an external JavaScript file. |
||||||
|
* @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated script tag |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function jsFile($url, $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'text/javascript'; |
||||||
|
$options['src'] = static::url($url); |
||||||
|
return static::tag('script', '', $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a form start tag. |
||||||
|
* @param array|string $action the form action URL. This parameter will be processed by [[url()]]. |
||||||
|
* @param string $method the form submission method, either "post" or "get" (case-insensitive) |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated form start tag. |
||||||
|
* @see endForm |
||||||
|
*/ |
||||||
|
public static function beginForm($action = '', $method = 'post', $options = array()) |
||||||
|
{ |
||||||
|
$action = static::url($action); |
||||||
|
|
||||||
|
// query parameters in the action are ignored for GET method |
||||||
|
// we use hidden fields to add them back |
||||||
|
$hiddens = array(); |
||||||
|
if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { |
||||||
|
foreach (explode('&', substr($action, $pos + 1)) as $pair) { |
||||||
|
if (($pos1 = strpos($pair, '=')) !== false) { |
||||||
|
$hiddens[] = static::hiddenInput(urldecode(substr($pair, 0, $pos1)), urldecode(substr($pair, $pos1 + 1))); |
||||||
|
} else { |
||||||
|
$hiddens[] = static::hiddenInput(urldecode($pair), ''); |
||||||
|
} |
||||||
|
} |
||||||
|
$action = substr($action, 0, $pos); |
||||||
|
} |
||||||
|
|
||||||
|
$options['action'] = $action; |
||||||
|
$options['method'] = $method; |
||||||
|
$form = static::beginTag('form', $options); |
||||||
|
if ($hiddens !== array()) { |
||||||
|
$form .= "\n" . implode("\n", $hiddens); |
||||||
|
} |
||||||
|
|
||||||
|
return $form; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a form end tag. |
||||||
|
* @return string the generated tag |
||||||
|
* @see beginForm |
||||||
|
*/ |
||||||
|
public static function endForm() |
||||||
|
{ |
||||||
|
return '</form>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a hyperlink tag. |
||||||
|
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]] |
||||||
|
* and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute |
||||||
|
* will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated hyperlink |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function a($text, $url = null, $options = array()) |
||||||
|
{ |
||||||
|
if ($url !== null) { |
||||||
|
$options['href'] = static::url($url); |
||||||
|
} |
||||||
|
return static::tag('a', $text, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a mailto hyperlink. |
||||||
|
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param string $email email address. If this is null, the first parameter (link body) will be treated |
||||||
|
* as the email address and used. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated mailto link |
||||||
|
*/ |
||||||
|
public static function mailto($text, $email = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::a($text, 'mailto:' . ($email === null ? $text : $email), $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an image tag. |
||||||
|
* @param string $src the image URL. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated image tag |
||||||
|
*/ |
||||||
|
public static function img($src, $options = array()) |
||||||
|
{ |
||||||
|
$options['src'] = static::url($src); |
||||||
|
if (!isset($options['alt'])) { |
||||||
|
$options['alt'] = ''; |
||||||
|
} |
||||||
|
return static::tag('img', null, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a label tag. |
||||||
|
* @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param string $for the ID of the HTML element that this label is associated with. |
||||||
|
* If this is null, the "for" attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated label tag |
||||||
|
*/ |
||||||
|
public static function label($content, $for = null, $options = array()) |
||||||
|
{ |
||||||
|
$options['for'] = $for; |
||||||
|
return static::tag('label', $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "button" will be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function button($name = null, $value = null, $content = 'Button', $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
$options['value'] = $value; |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'button'; |
||||||
|
} |
||||||
|
return static::tag('button', $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a submit button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated submit button tag |
||||||
|
*/ |
||||||
|
public static function submitButton($name = null, $value = null, $content = 'Submit', $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'submit'; |
||||||
|
return static::button($name, $value, $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a reset button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated reset button tag |
||||||
|
*/ |
||||||
|
public static function resetButton($name = null, $value = null, $content = 'Reset', $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'reset'; |
||||||
|
return static::button($name, $value, $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an input type of the given type. |
||||||
|
* @param string $type the type attribute. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated input tag |
||||||
|
*/ |
||||||
|
public static function input($type, $name = null, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = $type; |
||||||
|
$options['name'] = $name; |
||||||
|
$options['value'] = $value; |
||||||
|
return static::tag('input', null, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an input button. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function buttonInput($name, $value = 'Button', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('button', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a submit input button. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function submitInput($name = null, $value = 'Submit', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('submit', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a reset input button. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* Attributes whose value is null will be ignored and not put in the tag returned. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function resetInput($name = null, $value = 'Reset', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('reset', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a text input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function textInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('text', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a hidden input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function hiddenInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('hidden', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a password input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function passwordInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('password', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a file input field. |
||||||
|
* To use a file input field, you should set the enclosing form's "enctype" attribute to |
||||||
|
* be "multipart/form-data". After the form is submitted, the uploaded file information |
||||||
|
* can be obtained via $_FILES[$name] (see PHP documentation). |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function fileInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('file', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a text area input. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string $value the input value. Note that it will be encoded using [[encode()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated text area tag |
||||||
|
*/ |
||||||
|
public static function textarea($name, $value = '', $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
return static::tag('textarea', static::encode($value), $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a radio button input. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param boolean $checked whether the radio button should be checked. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute |
||||||
|
* is present, a hidden input will be generated so that if the radio button is not checked and is submitted, |
||||||
|
* the value of this attribute will still be submitted to the server via the hidden input. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated radio button tag |
||||||
|
*/ |
||||||
|
public static function radio($name, $checked = false, $value = '1', $options = array()) |
||||||
|
{ |
||||||
|
$options['checked'] = $checked; |
||||||
|
$options['value'] = $value; |
||||||
|
if (isset($options['uncheck'])) { |
||||||
|
// add a hidden field so that if the radio button is not selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||||
|
unset($options['uncheck']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
return $hidden . static::input('radio', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a checkbox input. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param boolean $checked whether the checkbox should be checked. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute |
||||||
|
* is present, a hidden input will be generated so that if the checkbox is not checked and is submitted, |
||||||
|
* the value of this attribute will still be submitted to the server via the hidden input. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated checkbox tag |
||||||
|
*/ |
||||||
|
public static function checkbox($name, $checked = false, $value = '1', $options = array()) |
||||||
|
{ |
||||||
|
$options['checked'] = $checked; |
||||||
|
$options['value'] = $value; |
||||||
|
if (isset($options['uncheck'])) { |
||||||
|
// add a hidden field so that if the checkbox is not selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||||
|
unset($options['uncheck']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
return $hidden . static::input('checkbox', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a drop-down list. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string $selection the selected value |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - prompt: string, a prompt text to be displayed as the first option; |
||||||
|
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||||
|
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* array( |
||||||
|
* 'value1' => array('disabled' => true), |
||||||
|
* 'value2' => array('label' => 'value 2'), |
||||||
|
* ); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||||
|
* except that the array keys represent the optgroup labels specified in $items. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated drop-down list tag |
||||||
|
*/ |
||||||
|
public static function dropDownList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
$selectOptions = static::renderSelectOptions($selection, $items, $options); |
||||||
|
return static::tag('select', "\n" . $selectOptions . "\n", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list box. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string|array $selection the selected value(s) |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - prompt: string, a prompt text to be displayed as the first option; |
||||||
|
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||||
|
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* array( |
||||||
|
* 'value1' => array('disabled' => true), |
||||||
|
* 'value2' => array('label' => 'value 2'), |
||||||
|
* ); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||||
|
* except that the array keys represent the optgroup labels specified in $items. |
||||||
|
* - unselect: string, the value that will be submitted when no option is selected. |
||||||
|
* When this attribute is set, a hidden field will be generated so that if no option is selected in multiple |
||||||
|
* mode, we can still obtain the posted unselect value. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated list box tag |
||||||
|
*/ |
||||||
|
public static function listBox($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['size'])) { |
||||||
|
$options['size'] = 4; |
||||||
|
} |
||||||
|
if (isset($options['multiple']) && $options['multiple'] && substr($name, -2) !== '[]') { |
||||||
|
$name .= '[]'; |
||||||
|
} |
||||||
|
$options['name'] = $name; |
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
if (substr($name, -2) === '[]') { |
||||||
|
$name = substr($name, 0, -2); |
||||||
|
} |
||||||
|
$hidden = static::hiddenInput($name, $options['unselect']); |
||||||
|
unset($options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
$selectOptions = static::renderSelectOptions($selection, $items, $options); |
||||||
|
return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list of checkboxes. |
||||||
|
* A checkbox list allows multiple selection, like [[listBox()]]. |
||||||
|
* As a result, the corresponding submitted value is an array. |
||||||
|
* @param string $name the name attribute of each checkbox. |
||||||
|
* @param string|array $selection the selected value(s). |
||||||
|
* @param array $items the data item used to generate the checkboxes. |
||||||
|
* The array keys are the labels, while the array values are the corresponding checkbox values. |
||||||
|
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||||
|
* @param array $options options (name => config) for the checkbox list. The following options are supported: |
||||||
|
* |
||||||
|
* - unselect: string, the value that should be submitted when none of the checkboxes is selected. |
||||||
|
* By setting this option, a hidden input will be generated. |
||||||
|
* - separator: string, the HTML code that separates items. |
||||||
|
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||||
|
* corresponding to a single item in $items. The signature of this callback must be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($index, $label, $name, $checked, $value) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where $index is the zero-based index of the checkbox in the whole list; $label |
||||||
|
* is the label for the checkbox; and $name, $value and $checked represent the name, |
||||||
|
* value and the checked status of the checkbox input. |
||||||
|
* @return string the generated checkbox list |
||||||
|
*/ |
||||||
|
public static function checkboxList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
if (substr($name, -2) !== '[]') { |
||||||
|
$name .= '[]'; |
||||||
|
} |
||||||
|
|
||||||
|
$formatter = isset($options['item']) ? $options['item'] : null; |
||||||
|
$lines = array(); |
||||||
|
$index = 0; |
||||||
|
foreach ($items as $value => $label) { |
||||||
|
$checked = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($value, $selection) |
||||||
|
|| is_array($selection) && in_array($value, $selection)); |
||||||
|
if ($formatter !== null) { |
||||||
|
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||||
|
} else { |
||||||
|
$lines[] = static::label(static::checkbox($name, $checked, $value) . ' ' . $label); |
||||||
|
} |
||||||
|
$index++; |
||||||
|
} |
||||||
|
|
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
$name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name; |
||||||
|
$hidden = static::hiddenInput($name2, $options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||||
|
|
||||||
|
return $hidden . implode($separator, $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list of radio buttons. |
||||||
|
* A radio button list is like a checkbox list, except that it only allows single selection. |
||||||
|
* @param string $name the name attribute of each radio button. |
||||||
|
* @param string|array $selection the selected value(s). |
||||||
|
* @param array $items the data item used to generate the radio buttons. |
||||||
|
* The array keys are the labels, while the array values are the corresponding radio button values. |
||||||
|
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||||
|
* @param array $options options (name => config) for the radio button list. The following options are supported: |
||||||
|
* |
||||||
|
* - unselect: string, the value that should be submitted when none of the radio buttons is selected. |
||||||
|
* By setting this option, a hidden input will be generated. |
||||||
|
* - separator: string, the HTML code that separates items. |
||||||
|
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||||
|
* corresponding to a single item in $items. The signature of this callback must be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($index, $label, $name, $checked, $value) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where $index is the zero-based index of the radio button in the whole list; $label |
||||||
|
* is the label for the radio button; and $name, $value and $checked represent the name, |
||||||
|
* value and the checked status of the radio button input. |
||||||
|
* @return string the generated radio button list |
||||||
|
*/ |
||||||
|
public static function radioList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
$formatter = isset($options['item']) ? $options['item'] : null; |
||||||
|
$lines = array(); |
||||||
|
$index = 0; |
||||||
|
foreach ($items as $value => $label) { |
||||||
|
$checked = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($value, $selection) |
||||||
|
|| is_array($selection) && in_array($value, $selection)); |
||||||
|
if ($formatter !== null) { |
||||||
|
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||||
|
} else { |
||||||
|
$lines[] = static::label(static::radio($name, $checked, $value) . ' ' . $label); |
||||||
|
} |
||||||
|
$index++; |
||||||
|
} |
||||||
|
|
||||||
|
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
|
||||||
|
return $hidden . implode($separator, $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]]. |
||||||
|
* @param string|array $selection the selected value(s). This can be either a string for single selection |
||||||
|
* or an array for multiple selections. |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call. |
||||||
|
* This method will take out these elements, if any: "prompt", "options" and "groups". See more details |
||||||
|
* in [[dropDownList()]] for the explanation of these elements. |
||||||
|
* |
||||||
|
* @return string the generated list options |
||||||
|
*/ |
||||||
|
public static function renderSelectOptions($selection, $items, &$tagOptions = array()) |
||||||
|
{ |
||||||
|
$lines = array(); |
||||||
|
if (isset($tagOptions['prompt'])) { |
||||||
|
$prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt'])); |
||||||
|
$lines[] = static::tag('option', $prompt, array('value' => '')); |
||||||
|
} |
||||||
|
|
||||||
|
$options = isset($tagOptions['options']) ? $tagOptions['options'] : array(); |
||||||
|
$groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array(); |
||||||
|
unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']); |
||||||
|
|
||||||
|
foreach ($items as $key => $value) { |
||||||
|
if (is_array($value)) { |
||||||
|
$groupAttrs = isset($groups[$key]) ? $groups[$key] : array(); |
||||||
|
$groupAttrs['label'] = $key; |
||||||
|
$attrs = array('options' => $options, 'groups' => $groups); |
||||||
|
$content = static::renderSelectOptions($selection, $value, $attrs); |
||||||
|
$lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs); |
||||||
|
} else { |
||||||
|
$attrs = isset($options[$key]) ? $options[$key] : array(); |
||||||
|
$attrs['value'] = $key; |
||||||
|
$attrs['selected'] = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($key, $selection) |
||||||
|
|| is_array($selection) && in_array($key, $selection)); |
||||||
|
$lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return implode("\n", $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the HTML tag attributes. |
||||||
|
* Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially |
||||||
|
* according to [[booleanAttributes]] and [[showBooleanAttributeValues]]. |
||||||
|
* @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. |
||||||
|
* Attributes whose value is null will be ignored and not put in the rendering result. |
||||||
|
* @return string the rendering result. If the attributes are not empty, they will be rendered |
||||||
|
* into a string with a leading white space (such that it can be directly appended to the tag name |
||||||
|
* in a tag. If there is no attribute, an empty string will be returned. |
||||||
|
*/ |
||||||
|
public static function renderTagAttributes($attributes) |
||||||
|
{ |
||||||
|
if (count($attributes) > 1) { |
||||||
|
$sorted = array(); |
||||||
|
foreach (static::$attributeOrder as $name) { |
||||||
|
if (isset($attributes[$name])) { |
||||||
|
$sorted[$name] = $attributes[$name]; |
||||||
|
} |
||||||
|
} |
||||||
|
$attributes = array_merge($sorted, $attributes); |
||||||
|
} |
||||||
|
|
||||||
|
$html = ''; |
||||||
|
foreach ($attributes as $name => $value) { |
||||||
|
if (isset(static::$booleanAttributes[strtolower($name)])) { |
||||||
|
if ($value || strcasecmp($name, $value) === 0) { |
||||||
|
$html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name"; |
||||||
|
} |
||||||
|
} elseif ($value !== null) { |
||||||
|
$html .= " $name=\"" . static::encode($value) . '"'; |
||||||
|
} |
||||||
|
} |
||||||
|
return $html; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Normalizes the input parameter to be a valid URL. |
||||||
|
* |
||||||
|
* If the input parameter |
||||||
|
* |
||||||
|
* - is an empty string: the currently requested URL will be returned; |
||||||
|
* - is a non-empty string: it will be processed by [[Yii::getAlias()]] and returned; |
||||||
|
* - is an array: the first array element is considered a route, while the rest of the name-value |
||||||
|
* pairs are treated as the parameters to be used for URL creation using [[\yii\web\Controller::createUrl()]]. |
||||||
|
* For example: `array('post/index', 'page' => 2)`, `array('index')`. |
||||||
|
* |
||||||
|
* @param array|string $url the parameter to be used to generate a valid URL |
||||||
|
* @return string the normalized URL |
||||||
|
* @throws InvalidParamException if the parameter is invalid. |
||||||
|
*/ |
||||||
|
public static function url($url) |
||||||
|
{ |
||||||
|
if (is_array($url)) { |
||||||
|
if (isset($url[0])) { |
||||||
|
$route = $url[0]; |
||||||
|
$params = array_splice($url, 1); |
||||||
|
if (Yii::$app->controller !== null) { |
||||||
|
return Yii::$app->controller->createUrl($route, $params); |
||||||
|
} else { |
||||||
|
return Yii::$app->getUrlManager()->createUrl($route, $params); |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new InvalidParamException('The array specifying a URL must contain at least one element.'); |
||||||
|
} |
||||||
|
} elseif ($url === '') { |
||||||
|
return Yii::$app->getRequest()->getUrl(); |
||||||
|
} else { |
||||||
|
return Yii::getAlias($url); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\Exception; |
||||||
|
use yii\base\InvalidConfigException; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* SecurityHelper provides a set of methods to handle common security-related tasks. |
||||||
|
* |
||||||
|
* In particular, SecurityHelper supports the following features: |
||||||
|
* |
||||||
|
* - Encryption/decryption: [[encrypt()]] and [[decrypt()]] |
||||||
|
* - Data tampering prevention: [[hashData()]] and [[validateData()]] |
||||||
|
* - Password validation: [[generatePasswordHash()]] and [[validatePassword()]] |
||||||
|
* |
||||||
|
* Additionally, SecurityHelper provides [[getSecretKey()]] to support generating |
||||||
|
* named secret keys. These secret keys, once generated, will be stored in a file |
||||||
|
* and made available in future requests. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Tom Worster <fsb@thefsb.org> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class SecurityHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Encrypts data. |
||||||
|
* @param string $data data to be encrypted. |
||||||
|
* @param string $key the encryption secret key |
||||||
|
* @return string the encrypted data |
||||||
|
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||||
|
* @see decrypt() |
||||||
|
*/ |
||||||
|
public static function encrypt($data, $key) |
||||||
|
{ |
||||||
|
$module = static::openCryptModule(); |
||||||
|
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||||
|
srand(); |
||||||
|
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); |
||||||
|
mcrypt_generic_init($module, $key, $iv); |
||||||
|
$encrypted = $iv . mcrypt_generic($module, $data); |
||||||
|
mcrypt_generic_deinit($module); |
||||||
|
mcrypt_module_close($module); |
||||||
|
return $encrypted; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decrypts data |
||||||
|
* @param string $data data to be decrypted. |
||||||
|
* @param string $key the decryption secret key |
||||||
|
* @return string the decrypted data |
||||||
|
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||||
|
* @see encrypt() |
||||||
|
*/ |
||||||
|
public static function decrypt($data, $key) |
||||||
|
{ |
||||||
|
$module = static::openCryptModule(); |
||||||
|
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||||
|
$ivSize = mcrypt_enc_get_iv_size($module); |
||||||
|
$iv = StringHelper::substr($data, 0, $ivSize); |
||||||
|
mcrypt_generic_init($module, $key, $iv); |
||||||
|
$decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data))); |
||||||
|
mcrypt_generic_deinit($module); |
||||||
|
mcrypt_module_close($module); |
||||||
|
return rtrim($decrypted, "\0"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prefixes data with a keyed hash value so that it can later be detected if it is tampered. |
||||||
|
* @param string $data the data to be protected |
||||||
|
* @param string $key the secret key to be used for generating hash |
||||||
|
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||||
|
* function to see the supported hashing algorithms on your system. |
||||||
|
* @return string the data prefixed with the keyed hash |
||||||
|
* @see validateData() |
||||||
|
* @see getSecretKey() |
||||||
|
*/ |
||||||
|
public static function hashData($data, $key, $algorithm = 'sha256') |
||||||
|
{ |
||||||
|
return hash_hmac($algorithm, $data, $key) . $data; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Validates if the given data is tampered. |
||||||
|
* @param string $data the data to be validated. The data must be previously |
||||||
|
* generated by [[hashData()]]. |
||||||
|
* @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]]. |
||||||
|
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||||
|
* function to see the supported hashing algorithms on your system. This must be the same |
||||||
|
* as the value passed to [[hashData()]] when generating the hash for the data. |
||||||
|
* @return string the real data with the hash stripped off. False if the data is tampered. |
||||||
|
* @see hashData() |
||||||
|
*/ |
||||||
|
public static function validateData($data, $key, $algorithm = 'sha256') |
||||||
|
{ |
||||||
|
$hashSize = StringHelper::strlen(hash_hmac($algorithm, 'test', $key)); |
||||||
|
$n = StringHelper::strlen($data); |
||||||
|
if ($n >= $hashSize) { |
||||||
|
$hash = StringHelper::substr($data, 0, $hashSize); |
||||||
|
$data2 = StringHelper::substr($data, $hashSize, $n - $hashSize); |
||||||
|
return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a secret key associated with the specified name. |
||||||
|
* If the secret key does not exist, a random key will be generated |
||||||
|
* and saved in the file "keys.php" under the application's runtime directory |
||||||
|
* so that the same secret key can be returned in future requests. |
||||||
|
* @param string $name the name that is associated with the secret key |
||||||
|
* @param integer $length the length of the key that should be generated if not exists |
||||||
|
* @return string the secret key associated with the specified name |
||||||
|
*/ |
||||||
|
public static function getSecretKey($name, $length = 32) |
||||||
|
{ |
||||||
|
static $keys; |
||||||
|
$keyFile = Yii::$app->getRuntimePath() . '/keys.php'; |
||||||
|
if ($keys === null) { |
||||||
|
$keys = is_file($keyFile) ? require($keyFile) : array(); |
||||||
|
} |
||||||
|
if (!isset($keys[$name])) { |
||||||
|
// generate a 32-char random key |
||||||
|
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
||||||
|
$keys[$name] = substr(str_shuffle(str_repeat($chars, 5)), 0, $length); |
||||||
|
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
|
||||||
|
} |
||||||
|
return $keys[$name]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens the mcrypt module. |
||||||
|
* @return resource the mcrypt module handle. |
||||||
|
* @throws InvalidConfigException if mcrypt extension is not installed |
||||||
|
* @throws Exception if mcrypt initialization fails |
||||||
|
*/ |
||||||
|
protected static function openCryptModule() |
||||||
|
{ |
||||||
|
if (!extension_loaded('mcrypt')) { |
||||||
|
throw new InvalidConfigException('The mcrypt PHP extension is not installed.'); |
||||||
|
} |
||||||
|
$module = @mcrypt_module_open('rijndael-256', '', MCRYPT_MODE_CBC, ''); |
||||||
|
if ($module === false) { |
||||||
|
throw new Exception('Failed to initialize the mcrypt module.'); |
||||||
|
} |
||||||
|
return $module; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a secure hash from a password and a random salt. |
||||||
|
* |
||||||
|
* The generated hash can be stored in database (e.g. `CHAR(64) CHARACTER SET latin1` on MySQL). |
||||||
|
* Later when a password needs to be validated, the hash can be fetched and passed |
||||||
|
* to [[validatePassword()]]. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* // generates the hash (usually done during user registration or when the password is changed) |
||||||
|
* $hash = SecurityHelper::hashPassword($password); |
||||||
|
* // ...save $hash in database... |
||||||
|
* |
||||||
|
* // during login, validate if the password entered is correct using $hash fetched from database |
||||||
|
* if (PasswordHelper::verifyPassword($password, $hash) { |
||||||
|
* // password is good |
||||||
|
* } else { |
||||||
|
* // password is bad |
||||||
|
* } |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param string $password The password to be hashed. |
||||||
|
* @param integer $cost Cost parameter used by the Blowfish hash algorithm. |
||||||
|
* The higher the value of cost, |
||||||
|
* the longer it takes to generate the hash and to verify a password against it. Higher cost |
||||||
|
* therefore slows down a brute-force attack. For best protection against brute for attacks, |
||||||
|
* set it to the highest value that is tolerable on production servers. The time taken to |
||||||
|
* compute the hash doubles for every increment by one of $cost. So, for example, if the |
||||||
|
* hash takes 1 second to compute when $cost is 14 then then the compute time varies as |
||||||
|
* 2^($cost - 14) seconds. |
||||||
|
* @throws Exception on bad password parameter or cost parameter |
||||||
|
* @return string The password hash string, ASCII and not longer than 64 characters. |
||||||
|
* @see validatePassword() |
||||||
|
*/ |
||||||
|
public static function generatePasswordHash($password, $cost = 13) |
||||||
|
{ |
||||||
|
$salt = static::generateSalt($cost); |
||||||
|
$hash = crypt($password, $salt); |
||||||
|
|
||||||
|
if (!is_string($hash) || strlen($hash) < 32) { |
||||||
|
throw new Exception('Unknown error occurred while generating hash.'); |
||||||
|
} |
||||||
|
|
||||||
|
return $hash; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Verifies a password against a hash. |
||||||
|
* @param string $password The password to verify. |
||||||
|
* @param string $hash The hash to verify the password against. |
||||||
|
* @return boolean whether the password is correct. |
||||||
|
* @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available. |
||||||
|
* @see generatePasswordHash() |
||||||
|
*/ |
||||||
|
public static function validatePassword($password, $hash) |
||||||
|
{ |
||||||
|
if (!is_string($password) || $password === '') { |
||||||
|
throw new InvalidParamException('Password must be a string and cannot be empty.'); |
||||||
|
} |
||||||
|
|
||||||
|
if (!preg_match('/^\$2[axy]\$(\d\d)\$[\./0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) { |
||||||
|
throw new InvalidParamException('Hash is invalid.'); |
||||||
|
} |
||||||
|
|
||||||
|
$test = crypt($password, $hash); |
||||||
|
$n = strlen($test); |
||||||
|
if (strlen($test) < 32 || $n !== strlen($hash)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Use a for-loop to compare two strings to prevent timing attacks. See: |
||||||
|
// http://codereview.stackexchange.com/questions/13512 |
||||||
|
$check = 0; |
||||||
|
for ($i = 0; $i < $n; ++$i) { |
||||||
|
$check |= (ord($test[$i]) ^ ord($hash[$i])); |
||||||
|
} |
||||||
|
|
||||||
|
return $check === 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a salt that can be used to generate a password hash. |
||||||
|
* |
||||||
|
* The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function |
||||||
|
* requires, for the Blowfish hash algorithm, a salt string in a specific format: |
||||||
|
* "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters |
||||||
|
* from the alphabet "./0-9A-Za-z". |
||||||
|
* |
||||||
|
* @param integer $cost the cost parameter |
||||||
|
* @return string the random salt value. |
||||||
|
* @throws InvalidParamException if the cost parameter is not between 4 and 30 |
||||||
|
*/ |
||||||
|
protected static function generateSalt($cost = 13) |
||||||
|
{ |
||||||
|
$cost = (int)$cost; |
||||||
|
if ($cost < 4 || $cost > 30) { |
||||||
|
throw new InvalidParamException('Cost must be between 4 and 31.'); |
||||||
|
} |
||||||
|
|
||||||
|
// Get 20 * 8bits of pseudo-random entropy from mt_rand(). |
||||||
|
$rand = ''; |
||||||
|
for ($i = 0; $i < 20; ++$i) { |
||||||
|
$rand .= chr(mt_rand(0, 255)); |
||||||
|
} |
||||||
|
|
||||||
|
// Add the microtime for a little more entropy. |
||||||
|
$rand .= microtime(); |
||||||
|
// Mix the bits cryptographically into a 20-byte binary string. |
||||||
|
$rand = sha1($rand, true); |
||||||
|
// Form the prefix that specifies Blowfish algorithm and cost parameter. |
||||||
|
$salt = sprintf("$2y$%02d$", $cost); |
||||||
|
// Append the random salt data in the required base64 format. |
||||||
|
$salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22)); |
||||||
|
return $salt; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* StringHelper |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Alex Makarov <sam@rmcreative.ru> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class StringHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Returns the number of bytes in the given string. |
||||||
|
* This method ensures the string is treated as a byte array. |
||||||
|
* It will use `mb_strlen()` if it is available. |
||||||
|
* @param string $string the string being measured for length |
||||||
|
* @return integer the number of bytes in the given string. |
||||||
|
*/ |
||||||
|
public static function strlen($string) |
||||||
|
{ |
||||||
|
return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the portion of string specified by the start and length parameters. |
||||||
|
* This method ensures the string is treated as a byte array. |
||||||
|
* It will use `mb_substr()` if it is available. |
||||||
|
* @param string $string the input string. Must be one character or longer. |
||||||
|
* @param integer $start the starting position |
||||||
|
* @param integer $length the desired portion length |
||||||
|
* @return string the extracted part of string, or FALSE on failure or an empty string. |
||||||
|
* @see http://www.php.net/manual/en/function.substr.php |
||||||
|
*/ |
||||||
|
public static function substr($string, $start, $length) |
||||||
|
{ |
||||||
|
return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a word to its plural form. |
||||||
|
* Note that this is for English only! |
||||||
|
* For example, 'apple' will become 'apples', and 'child' will become 'children'. |
||||||
|
* @param string $name the word to be pluralized |
||||||
|
* @return string the pluralized word |
||||||
|
*/ |
||||||
|
public static function pluralize($name) |
||||||
|
{ |
||||||
|
static $rules = array( |
||||||
|
'/(m)ove$/i' => '\1oves', |
||||||
|
'/(f)oot$/i' => '\1eet', |
||||||
|
'/(c)hild$/i' => '\1hildren', |
||||||
|
'/(h)uman$/i' => '\1umans', |
||||||
|
'/(m)an$/i' => '\1en', |
||||||
|
'/(s)taff$/i' => '\1taff', |
||||||
|
'/(t)ooth$/i' => '\1eeth', |
||||||
|
'/(p)erson$/i' => '\1eople', |
||||||
|
'/([m|l])ouse$/i' => '\1ice', |
||||||
|
'/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es', |
||||||
|
'/([^aeiouy]|qu)y$/i' => '\1ies', |
||||||
|
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', |
||||||
|
'/(shea|lea|loa|thie)f$/i' => '\1ves', |
||||||
|
'/([ti])um$/i' => '\1a', |
||||||
|
'/(tomat|potat|ech|her|vet)o$/i' => '\1oes', |
||||||
|
'/(bu)s$/i' => '\1ses', |
||||||
|
'/(ax|test)is$/i' => '\1es', |
||||||
|
'/s$/' => 's', |
||||||
|
); |
||||||
|
foreach ($rules as $rule => $replacement) { |
||||||
|
if (preg_match($rule, $name)) { |
||||||
|
return preg_replace($rule, $replacement, $name); |
||||||
|
} |
||||||
|
} |
||||||
|
return $name . 's'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a CamelCase name into space-separated words. |
||||||
|
* For example, 'PostTag' will be converted to 'Post Tag'. |
||||||
|
* @param string $name the string to be converted |
||||||
|
* @param boolean $ucwords whether to capitalize the first letter in each word |
||||||
|
* @return string the resulting words |
||||||
|
*/ |
||||||
|
public static function camel2words($name, $ucwords = true) |
||||||
|
{ |
||||||
|
$label = trim(strtolower(str_replace(array('-', '_', '.'), ' ', preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))); |
||||||
|
return $ucwords ? ucwords($label) : $label; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a CamelCase name into an ID in lowercase. |
||||||
|
* Words in the ID may be concatenated using the specified character (defaults to '-'). |
||||||
|
* For example, 'PostTag' will be converted to 'post-tag'. |
||||||
|
* @param string $name the string to be converted |
||||||
|
* @param string $separator the character used to concatenate the words in the ID |
||||||
|
* @return string the resulting ID |
||||||
|
*/ |
||||||
|
public static function camel2id($name, $separator = '-') |
||||||
|
{ |
||||||
|
if ($separator === '_') { |
||||||
|
return trim(strtolower(preg_replace('/(?<![A-Z])[A-Z]/', '_\0', $name)), '_'); |
||||||
|
} else { |
||||||
|
return trim(strtolower(str_replace('_', $separator, preg_replace('/(?<![A-Z])[A-Z]/', $separator . '\0', $name))), $separator); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts an ID into a CamelCase name. |
||||||
|
* Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name. |
||||||
|
* For example, 'post-tag' is converted to 'PostTag'. |
||||||
|
* @param string $id the ID to be converted |
||||||
|
* @param string $separator the character used to separate the words in the ID |
||||||
|
* @return string the resulting CamelCase name |
||||||
|
*/ |
||||||
|
public static function id2camel($id, $separator = '-') |
||||||
|
{ |
||||||
|
return str_replace(' ', '', ucwords(implode(' ', explode($separator, $id)))); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* VarDumper is intended to replace the buggy PHP function var_dump and print_r. |
||||||
|
* It can correctly identify the recursively referenced objects in a complex |
||||||
|
* object structure. It also has a recursive depth control to avoid indefinite |
||||||
|
* recursive display of some peculiar variables. |
||||||
|
* |
||||||
|
* VarDumper can be used as follows, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* VarDumper::dump($var); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class VarDumper |
||||||
|
{ |
||||||
|
private static $_objects; |
||||||
|
private static $_output; |
||||||
|
private static $_depth; |
||||||
|
|
||||||
|
/** |
||||||
|
* Displays a variable. |
||||||
|
* This method achieves the similar functionality as var_dump and print_r |
||||||
|
* but is more robust when handling complex objects such as Yii controllers. |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||||
|
* @param boolean $highlight whether the result should be syntax-highlighted |
||||||
|
*/ |
||||||
|
public static function dump($var, $depth = 10, $highlight = false) |
||||||
|
{ |
||||||
|
echo self::dumpAsString($var, $depth, $highlight); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Dumps a variable in terms of a string. |
||||||
|
* This method achieves the similar functionality as var_dump and print_r |
||||||
|
* but is more robust when handling complex objects such as Yii controllers. |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||||
|
* @param boolean $highlight whether the result should be syntax-highlighted |
||||||
|
* @return string the string representation of the variable |
||||||
|
*/ |
||||||
|
public static function dumpAsString($var, $depth = 10, $highlight = false) |
||||||
|
{ |
||||||
|
self::$_output = ''; |
||||||
|
self::$_objects = array(); |
||||||
|
self::$_depth = $depth; |
||||||
|
self::dumpInternal($var, 0); |
||||||
|
if ($highlight) { |
||||||
|
$result = highlight_string("<?php\n" . self::$_output, true);
|
||||||
|
self::$_output = preg_replace('/<\\?php<br \\/>/', '', $result, 1); |
||||||
|
} |
||||||
|
return self::$_output; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $level depth level |
||||||
|
*/ |
||||||
|
private static function dumpInternal($var, $level) |
||||||
|
{ |
||||||
|
switch (gettype($var)) { |
||||||
|
case 'boolean': |
||||||
|
self::$_output .= $var ? 'true' : 'false'; |
||||||
|
break; |
||||||
|
case 'integer': |
||||||
|
self::$_output .= "$var"; |
||||||
|
break; |
||||||
|
case 'double': |
||||||
|
self::$_output .= "$var"; |
||||||
|
break; |
||||||
|
case 'string': |
||||||
|
self::$_output .= "'" . addslashes($var) . "'"; |
||||||
|
break; |
||||||
|
case 'resource': |
||||||
|
self::$_output .= '{resource}'; |
||||||
|
break; |
||||||
|
case 'NULL': |
||||||
|
self::$_output .= "null"; |
||||||
|
break; |
||||||
|
case 'unknown type': |
||||||
|
self::$_output .= '{unknown}'; |
||||||
|
break; |
||||||
|
case 'array': |
||||||
|
if (self::$_depth <= $level) { |
||||||
|
self::$_output .= 'array(...)'; |
||||||
|
} elseif (empty($var)) { |
||||||
|
self::$_output .= 'array()'; |
||||||
|
} else { |
||||||
|
$keys = array_keys($var); |
||||||
|
$spaces = str_repeat(' ', $level * 4); |
||||||
|
self::$_output .= "array\n" . $spaces . '('; |
||||||
|
foreach ($keys as $key) { |
||||||
|
self::$_output .= "\n" . $spaces . ' '; |
||||||
|
self::dumpInternal($key, 0); |
||||||
|
self::$_output .= ' => '; |
||||||
|
self::dumpInternal($var[$key], $level + 1); |
||||||
|
} |
||||||
|
self::$_output .= "\n" . $spaces . ')'; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 'object': |
||||||
|
if (($id = array_search($var, self::$_objects, true)) !== false) { |
||||||
|
self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)'; |
||||||
|
} elseif (self::$_depth <= $level) { |
||||||
|
self::$_output .= get_class($var) . '(...)'; |
||||||
|
} else { |
||||||
|
$id = self::$_objects[] = $var; |
||||||
|
$className = get_class($var); |
||||||
|
$members = (array)$var; |
||||||
|
$spaces = str_repeat(' ', $level * 4); |
||||||
|
self::$_output .= "$className#$id\n" . $spaces . '('; |
||||||
|
foreach ($members as $key => $value) { |
||||||
|
$keyDisplay = strtr(trim($key), array("\0" => ':')); |
||||||
|
self::$_output .= "\n" . $spaces . " [$keyDisplay] => "; |
||||||
|
self::dumpInternal($value, $level + 1); |
||||||
|
} |
||||||
|
self::$_output .= "\n" . $spaces . ')'; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue