From 76cab3ea6449ef90bbfade7683b1c83aa2336a35 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 28 Sep 2013 02:01:06 +0400 Subject: [PATCH 1/4] Renamed base helper classes --- framework/yii/classes.php | 22 +- framework/yii/helpers/AbstractArrayHelper.php | 451 +++++++ framework/yii/helpers/AbstractConsole.php | 835 +++++++++++++ framework/yii/helpers/AbstractFileHelper.php | 329 +++++ framework/yii/helpers/AbstractHtml.php | 1599 ++++++++++++++++++++++++ framework/yii/helpers/AbstractHtmlPurifier.php | 34 + framework/yii/helpers/AbstractInflector.php | 480 +++++++ framework/yii/helpers/AbstractJson.php | 112 ++ framework/yii/helpers/AbstractMarkdown.php | 44 + framework/yii/helpers/AbstractSecurity.php | 285 +++++ framework/yii/helpers/AbstractStringHelper.php | 138 ++ framework/yii/helpers/AbstractVarDumper.php | 127 ++ framework/yii/helpers/ArrayHelper.php | 2 +- framework/yii/helpers/ArrayHelperBase.php | 451 ------- framework/yii/helpers/Console.php | 2 +- framework/yii/helpers/ConsoleBase.php | 835 ------------- framework/yii/helpers/FileHelper.php | 2 +- framework/yii/helpers/FileHelperBase.php | 329 ----- framework/yii/helpers/Html.php | 2 +- framework/yii/helpers/HtmlBase.php | 1599 ------------------------ framework/yii/helpers/HtmlPurifier.php | 2 +- framework/yii/helpers/HtmlPurifierBase.php | 34 - framework/yii/helpers/Inflector.php | 2 +- framework/yii/helpers/InflectorBase.php | 480 ------- framework/yii/helpers/Json.php | 2 +- framework/yii/helpers/JsonBase.php | 112 -- framework/yii/helpers/Markdown.php | 2 +- framework/yii/helpers/MarkdownBase.php | 44 - framework/yii/helpers/Security.php | 2 +- framework/yii/helpers/SecurityBase.php | 285 ----- framework/yii/helpers/StringHelper.php | 2 +- framework/yii/helpers/StringHelperBase.php | 138 -- framework/yii/helpers/VarDumper.php | 2 +- framework/yii/helpers/VarDumperBase.php | 127 -- 34 files changed, 4456 insertions(+), 4456 deletions(-) create mode 100644 framework/yii/helpers/AbstractArrayHelper.php create mode 100644 framework/yii/helpers/AbstractConsole.php create mode 100644 framework/yii/helpers/AbstractFileHelper.php create mode 100644 framework/yii/helpers/AbstractHtml.php create mode 100644 framework/yii/helpers/AbstractHtmlPurifier.php create mode 100644 framework/yii/helpers/AbstractInflector.php create mode 100644 framework/yii/helpers/AbstractJson.php create mode 100644 framework/yii/helpers/AbstractMarkdown.php create mode 100644 framework/yii/helpers/AbstractSecurity.php create mode 100644 framework/yii/helpers/AbstractStringHelper.php create mode 100644 framework/yii/helpers/AbstractVarDumper.php delete mode 100644 framework/yii/helpers/ArrayHelperBase.php delete mode 100644 framework/yii/helpers/ConsoleBase.php delete mode 100644 framework/yii/helpers/FileHelperBase.php delete mode 100644 framework/yii/helpers/HtmlBase.php delete mode 100644 framework/yii/helpers/HtmlPurifierBase.php delete mode 100644 framework/yii/helpers/InflectorBase.php delete mode 100644 framework/yii/helpers/JsonBase.php delete mode 100644 framework/yii/helpers/MarkdownBase.php delete mode 100644 framework/yii/helpers/SecurityBase.php delete mode 100644 framework/yii/helpers/StringHelperBase.php delete mode 100644 framework/yii/helpers/VarDumperBase.php diff --git a/framework/yii/classes.php b/framework/yii/classes.php index 40ca225..22472d7 100644 --- a/framework/yii/classes.php +++ b/framework/yii/classes.php @@ -125,27 +125,27 @@ return array( 'yii\grid\GridViewAsset' => YII_PATH . '/grid/GridViewAsset.php', 'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php', 'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php', - 'yii\helpers\ArrayHelperBase' => YII_PATH . '/helpers/ArrayHelperBase.php', + 'yii\helpers\AbstractArrayHelper' => YII_PATH . '/helpers/AbstractArrayHelper.php', 'yii\helpers\Console' => YII_PATH . '/helpers/Console.php', - 'yii\helpers\ConsoleBase' => YII_PATH . '/helpers/ConsoleBase.php', + 'yii\helpers\AbstractConsole' => YII_PATH . '/helpers/AbstractConsole.php', 'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php', - 'yii\helpers\FileHelperBase' => YII_PATH . '/helpers/FileHelperBase.php', + 'yii\helpers\AbstractFileHelper' => YII_PATH . '/helpers/AbstractFileHelper.php', 'yii\helpers\Html' => YII_PATH . '/helpers/Html.php', - 'yii\helpers\HtmlBase' => YII_PATH . '/helpers/HtmlBase.php', + 'yii\helpers\AbstractHtml' => YII_PATH . '/helpers/AbstractHtml.php', 'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php', - 'yii\helpers\HtmlPurifierBase' => YII_PATH . '/helpers/HtmlPurifierBase.php', + 'yii\helpers\AbstractHtmlPurifier' => YII_PATH . '/helpers/AbstractHtmlPurifier.php', 'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php', - 'yii\helpers\InflectorBase' => YII_PATH . '/helpers/InflectorBase.php', + 'yii\helpers\AbstractInflector' => YII_PATH . '/helpers/AbstractInflector.php', 'yii\helpers\Json' => YII_PATH . '/helpers/Json.php', - 'yii\helpers\JsonBase' => YII_PATH . '/helpers/JsonBase.php', + 'yii\helpers\AbstractJson' => YII_PATH . '/helpers/AbstractJson.php', 'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php', - 'yii\helpers\MarkdownBase' => YII_PATH . '/helpers/MarkdownBase.php', + 'yii\helpers\AbstractMarkdown' => YII_PATH . '/helpers/AbstractMarkdown.php', 'yii\helpers\Security' => YII_PATH . '/helpers/Security.php', - 'yii\helpers\SecurityBase' => YII_PATH . '/helpers/SecurityBase.php', + 'yii\helpers\AbstractSecurity' => YII_PATH . '/helpers/AbstractSecurity.php', 'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php', - 'yii\helpers\StringHelperBase' => YII_PATH . '/helpers/StringHelperBase.php', + 'yii\helpers\AbstractStringHelper' => YII_PATH . '/helpers/AbstractStringHelper.php', 'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php', - 'yii\helpers\VarDumperBase' => YII_PATH . '/helpers/VarDumperBase.php', + 'yii\helpers\AbstractVarDumper' => YII_PATH . '/helpers/AbstractVarDumper.php', 'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php', 'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php', 'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php', diff --git a/framework/yii/helpers/AbstractArrayHelper.php b/framework/yii/helpers/AbstractArrayHelper.php new file mode 100644 index 0000000..c26c1cd --- /dev/null +++ b/framework/yii/helpers/AbstractArrayHelper.php @@ -0,0 +1,451 @@ + + * @since 2.0 + */ +abstract class AbstractArrayHelper +{ + /** + * Converts an object or an array of objects into an array. + * @param object|array $object the object to be converted into an array + * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays. + * The properties specified for each class is an array of the following format: + * + * ~~~ + * array( + * 'app\models\Post' => array( + * 'id', + * 'title', + * // the key name in array result => property name + * 'createTime' => 'create_time', + * // the key name in array result => anonymous function + * 'length' => function ($post) { + * return strlen($post->content); + * }, + * ), + * ) + * ~~~ + * + * The result of `ArrayHelper::toArray($post, $properties)` could be like the following: + * + * ~~~ + * array( + * 'id' => 123, + * 'title' => 'test', + * 'createTime' => '2013-01-01 12:00AM', + * 'length' => 301, + * ) + * ~~~ + * + * @param boolean $recursive whether to recursively converts properties which are objects into arrays. + * @return array the array representation of the object + */ + public static function toArray($object, $properties = array(), $recursive = true) + { + if (!empty($properties) && is_object($object)) { + $className = get_class($object); + if (!empty($properties[$className])) { + $result = array(); + foreach ($properties[$className] as $key => $name) { + if (is_int($key)) { + $result[$name] = $object->$name; + } else { + $result[$key] = static::getValue($object, $name); + } + } + return $result; + } + } + if ($object instanceof Arrayable) { + $object = $object->toArray(); + if (!$recursive) { + return $object; + } + } + $result = array(); + foreach ($object as $key => $value) { + if ($recursive && (is_array($value) || is_object($value))) { + $result[$key] = static::toArray($value, true); + } else { + $result[$key] = $value; + } + } + return $result; + } + + /** + * 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 (!empty($args)) { + $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 element if found, default value otherwise + */ + 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; + } + } + + /** + * Removes an item from an array and returns the value. If the key does not exist in the array, the default value + * will be returned instead. + * + * Usage examples, + * + * ~~~ + * // $array = array('type' => 'A', 'options' => array(1, 2)); + * // working with array + * $type = \yii\helpers\ArrayHelper::remove($array, 'type'); + * // $array content + * // $array = array('options' => array(1, 2)); + * ~~~ + * + * @param array $array the array to extract value from + * @param string $key key name of the array element + * @param mixed $default the default value to be returned if the specified key does not exist + * @return mixed|null the value of the element if found, default value otherwise + */ + public static function remove(&$array, $key, $default = null) + { + if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { + $value = $array[$key]; + unset($array[$key]); + return $value; + } + return $default; + } + + /** + * 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 $descending whether to sort in descending or ascending order. When + * sorting by multiple keys with different descending orders, use an array of descending flags. + * @param integer|array $sortFlag the PHP sort flag. Valid values include + * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`. + * Please refer to [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. + * @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter + * is used only when `$sortFlag` is `SORT_STRING`. + * When sorting by multiple keys with different case sensitivities, use an array of boolean values. + * @throws InvalidParamException if the $descending or $sortFlag parameters do not have + * correct number of elements as that of $key. + */ + public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true) + { + $keys = is_array($key) ? $key : array($key); + if (empty($keys) || empty($array)) { + return; + } + $n = count($keys); + if (is_scalar($descending)) { + $descending = array_fill(0, $n, $descending); + } elseif (count($descending) !== $n) { + throw new InvalidParamException('The length of $descending 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 $sortFlag parameter must be the same as that of $keys.'); + } + if (is_scalar($caseSensitive)) { + $caseSensitive = array_fill(0, $n, $caseSensitive); + } elseif (count($caseSensitive) !== $n) { + throw new InvalidParamException('The length of $caseSensitive parameter must be the same as that of $keys.'); + } + $args = array(); + foreach ($keys as $i => $key) { + $flag = $sortFlag[$i]; + $cs = $caseSensitive[$i]; + if (!$cs && ($flag === SORT_STRING)) { + if (defined('SORT_FLAG_CASE')) { + $flag = $flag | SORT_FLAG_CASE; + $args[] = static::getColumn($array, $key); + } else { + $column = array(); + foreach (static::getColumn($array, $key) as $k => $value) { + $column[$k] = mb_strtolower($value); + } + $args[] = $column; + } + } else { + $args[] = static::getColumn($array, $key); + } + $args[] = $descending[$i] ? SORT_DESC : SORT_ASC; + $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; + } +} diff --git a/framework/yii/helpers/AbstractConsole.php b/framework/yii/helpers/AbstractConsole.php new file mode 100644 index 0000000..8131aae --- /dev/null +++ b/framework/yii/helpers/AbstractConsole.php @@ -0,0 +1,835 @@ + + * @since 2.0 + */ +abstract class AbstractConsole +{ + 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 RESET = 0; + const NORMAL = 0; + 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"; + } + + /** + * Returns the ANSI format code. + * + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @return string The ANSI format code according to the given formatting constants. + */ + public static function ansiFormatCode($format) + { + return "\033[" . implode(';', $format) . 'm'; + } + + /** + * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards. + * + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @see ansiFormatCode() + * @see ansiFormatEnd() + */ + public static function beginAnsiFormat($format) + { + echo "\033[" . implode(';', $format) . 'm'; + } + + /** + * Resets any ANSI format set by previous method [[ansiFormatBegin()]] + * Any output after this will have default text format. + * This is equal to calling + * + * ```php + * echo Console::ansiFormatCode(array(Console::RESET)) + * ``` + */ + public static function endAnsiFormat() + { + echo "\033[0m"; + } + + /** + * Will return a string formatted with the given ANSI style + * + * @param string $string the string to be formatted + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @return string + */ + public static function ansiFormat($string, $format = array()) + { + $code = implode(';', $format); + return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; + } + + /** + * Returns the ansi format code for xterm foreground color. + * You can pass the return value of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermFgColor($colorCode) + { + return '38;5;' . $colorCode; + } + + /** + * Returns the ansi format code for xterm background color. + * You can pass the return value of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermBgColor($colorCode) + { + return '48;5;' . $colorCode; + } + + /** + * Strips ANSI control codes from a string + * + * @param string $string String to strip + * @return string + */ + public static function stripAnsiFormat($string) + { + return preg_replace('/\033\[[\d;?]*\w/', '', $string); + } + + /** + * Converts an ANSI formatted string to HTML + * @param $string + * @return mixed + */ + // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 + 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 self::FG_BLACK: + $style = array('color' => '#000000'); + break; + case self::FG_BLUE: + $style = array('color' => '#000078'); + break; + case self::FG_CYAN: + $style = array('color' => '#007878'); + break; + case self::FG_GREEN: + $style = array('color' => '#007800'); + break; + case self::FG_GREY: + $style = array('color' => '#787878'); + break; + case self::FG_PURPLE: + $style = array('color' => '#780078'); + break; + case self::FG_RED: + $style = array('color' => '#780000'); + break; + case self::FG_YELLOW: + $style = array('color' => '#787800'); + break; + case self::BG_BLACK: + $style = array('background-color' => '#000000'); + break; + case self::BG_BLUE: + $style = array('background-color' => '#000078'); + break; + case self::BG_CYAN: + $style = array('background-color' => '#007878'); + break; + case self::BG_GREEN: + $style = array('background-color' => '#007800'); + break; + case self::BG_GREY: + $style = array('background-color' => '#787878'); + break; + case self::BG_PURPLE: + $style = array('background-color' => '#780078'); + break; + case self::BG_RED: + $style = array('background-color' => '#780000'); + break; + case self::BG_YELLOW: + $style = array('background-color' => '#787800'); + break; + case self::BOLD: + $style = array('font-weight' => 'bold'); + break; + case self::ITALIC: + $style = array('font-style' => 'italic'); + break; + case self::UNDERLINE: + $style = array('text-decoration' => array('underline')); + break; + case self::OVERLINED: + $style = array('text-decoration' => array('overline')); + break; + case self::CROSSED_OUT: + $style = array('text-decoration' => array('line-through')); + break; + case self::BLINK: + $style = array('text-decoration' => array('blink')); + break; + case self::NEGATIVE: // ??? + case self::CONCEALED: + case self::ENCIRCLED: + case self::FRAMED: + // TODO allow resetting codes + break; + case 0: // ansi reset + $return = ''; + for ($n = $tags; $tags > 0; $tags--) { + $return .= ''; + } + 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 ' $value) { + echo " $key - $value\n"; + } + echo " ? - Show help\n"; + goto top; + } elseif (!in_array($input, array_keys($options))) { + goto top; + } + return $input; + } + + /** + * Displays and updates a simple progress bar on screen. + * + * @param integer $done the number of items that are completed + * @param integer $total the total value of items that are to be done + * @param integer $size the size of the status bar (optional) + * @see http://snipplr.com/view/29548/ + */ + public static function showProgress($done, $total, $size = 30) + { + static $start; + + // if we go over our bound, just ignore it + if ($done > $total) { + return; + } + + if (empty($start)) { + $start = time(); + } + + $now = time(); + + $percent = (double)($done / $total); + $bar = floor($percent * $size); + + $status = "\r["; + $status .= str_repeat("=", $bar); + if ($bar < $size) { + $status .= ">"; + $status .= str_repeat(" ", $size - $bar); + } else { + $status .= "="; + } + + $display = number_format($percent * 100, 0); + + $status .= "] $display% $done/$total"; + + $rate = ($now - $start) / $done; + $left = $total - $done; + $eta = round($rate * $left, 2); + + $elapsed = $now - $start; + + $status .= " remaining: " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec."; + + static::stdout("$status "); + + flush(); + + // when done, send a newline + if ($done == $total) { + echo "\n"; + } + } +} diff --git a/framework/yii/helpers/AbstractFileHelper.php b/framework/yii/helpers/AbstractFileHelper.php new file mode 100644 index 0000000..5eab927 --- /dev/null +++ b/framework/yii/helpers/AbstractFileHelper.php @@ -0,0 +1,329 @@ + + * @author Alex Makarov + * @since 2.0 + */ +abstract class AbstractFileHelper +{ + /** + * 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 the 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. + * + * @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); + finfo_close($info); + if ($result !== false) { + return $result; + } + } + } + + return $checkExtension ? static::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) + { + static $mimeTypes = array(); + if ($magicFile === null) { + $magicFile = __DIR__ . '/mimeTypes.php'; + } + if (!isset($mimeTypes[$magicFile])) { + $mimeTypes[$magicFile] = require($magicFile); + } + if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { + $ext = strtolower($ext); + if (isset($mimeTypes[$magicFile][$ext])) { + return $mimeTypes[$magicFile][$ext]; + } + } + return null; + } + + /** + * Copies a whole directory as another one. + * The files and sub-directories will also be copied over. + * @param string $src the source directory + * @param string $dst the destination directory + * @param array $options options for directory copy. Valid options are: + * + * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775. + * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. + * - filter: callback, a PHP callback that is called for each directory or file. + * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. + * The callback can return one of the following values: + * + * * true: the directory or file will be copied (the "only" and "except" options will be ignored) + * * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored) + * * null: the "only" and "except" options will determine whether the directory or file should be copied + * + * - only: array, list of patterns that the file paths should match if they want to be copied. + * A path matches a pattern if it contains the pattern string at its end. + * For example, '.php' matches all file paths ending with '.php'. + * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. + * If a file path matches a pattern in both "only" and "except", it will NOT be copied. + * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied. + * A path matches a pattern if it contains the pattern string at its end. + * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' + * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; + * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches + * both '/' and '\' in the paths. + * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true. + * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied. + * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or + * file copied from, while `$to` is the copy target. + */ + public static function copyDirectory($src, $dst, $options = array()) + { + if (!is_dir($dst)) { + static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true); + } + + $handle = opendir($src); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $from = $src . DIRECTORY_SEPARATOR . $file; + $to = $dst . DIRECTORY_SEPARATOR . $file; + if (static::filterPath($from, $options)) { + if (is_file($from)) { + copy($from, $to); + if (isset($options['fileMode'])) { + @chmod($to, $options['fileMode']); + } + } else { + static::copyDirectory($from, $to, $options); + } + if (isset($options['afterCopy'])) { + call_user_func($options['afterCopy'], $from, $to); + } + } + } + closedir($handle); + } + + /** + * Removes a directory (and all its content) recursively. + * @param string $dir the directory to be deleted recursively. + */ + public static function removeDirectory($dir) + { + if (!is_dir($dir) || !($handle = opendir($dir))) { + return; + } + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_file($path)) { + unlink($path); + } else { + static::removeDirectory($path); + } + } + closedir($handle); + rmdir($dir); + } + + /** + * Returns the files found under the specified directory and subdirectories. + * @param string $dir the directory under which the files will be looked for. + * @param array $options options for file searching. Valid options are: + * + * - filter: callback, a PHP callback that is called for each directory or file. + * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. + * The callback can return one of the following values: + * + * * true: the directory or file will be returned (the "only" and "except" options will be ignored) + * * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored) + * * null: the "only" and "except" options will determine whether the directory or file should be returned + * + * - only: array, list of patterns that the file paths should match if they want to be returned. + * A path matches a pattern if it contains the pattern string at its end. + * For example, '.php' matches all file paths ending with '.php'. + * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. + * If a file path matches a pattern in both "only" and "except", it will NOT be returned. + * - except: array, list of patterns that the file paths or directory paths should match if they want to be excluded from the result. + * A path matches a pattern if it contains the pattern string at its end. + * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' + * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; + * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches + * both '/' and '\' in the paths. + * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. + * @return array files found under the directory. The file list is sorted. + */ + public static function findFiles($dir, $options = array()) + { + $list = array(); + $handle = opendir($dir); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (static::filterPath($path, $options)) { + if (is_file($path)) { + $list[] = $path; + } elseif (!isset($options['recursive']) || $options['recursive']) { + $list = array_merge($list, static::findFiles($path, $options)); + } + } + } + closedir($handle); + return $list; + } + + /** + * Checks if the given file path satisfies the filtering options. + * @param string $path the path of the file or directory to be checked + * @param array $options the filtering options. See [[findFiles()]] for explanations of + * the supported options. + * @return boolean whether the file or directory satisfies the filtering options. + */ + public static function filterPath($path, $options) + { + if (isset($options['filter'])) { + $result = call_user_func($options['filter'], $path); + if (is_bool($result)) { + return $result; + } + } + $path = str_replace('\\', '/', $path); + if ($isDir = is_dir($path)) { + $path .= '/'; + } + $n = StringHelper::strlen($path); + + if (!empty($options['except'])) { + foreach ($options['except'] as $name) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { + return false; + } + } + } + + if (!$isDir && !empty($options['only'])) { + foreach ($options['only'] as $name) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { + return true; + } + } + return false; + } + return true; + } + + /** + * Creates a new directory. + * + * This method is similar to the PHP `mkdir()` function except that + * it uses `chmod()` to set the permission of the created directory + * in order to avoid the impact of the `umask` setting. + * + * @param string $path path of the directory to be created. + * @param integer $mode the permission to be set for the created directory. + * @param boolean $recursive whether to create parent directories if they do not exist. + * @return boolean whether the directory is created successfully + */ + public static function createDirectory($path, $mode = 0775, $recursive = true) + { + if (is_dir($path)) { + return true; + } + $parentDir = dirname($path); + if ($recursive && !is_dir($parentDir)) { + static::createDirectory($parentDir, $mode, true); + } + $result = mkdir($path, $mode); + chmod($path, $mode); + return $result; + } +} diff --git a/framework/yii/helpers/AbstractHtml.php b/framework/yii/helpers/AbstractHtml.php new file mode 100644 index 0000000..37e926c --- /dev/null +++ b/framework/yii/helpers/AbstractHtml.php @@ -0,0 +1,1599 @@ + + * @since 2.0 + */ +abstract class AbstractHtml +{ + /** + * @var array list of void elements (element name => 1) + * @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 array the preferred order of attributes in a tag. This mainly affects the order of the attributes + * that are rendered by [[renderTagAttributes()]]. + */ + 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 + * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false, + * HTML entities in `$content` will not be further encoded. + * @return string the encoded content + * @see decode + * @see http://www.php.net/manual/en/function.htmlspecialchars.php + */ + public static function encode($content, $doubleEncode = true) + { + return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode); + } + + /** + * 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) . '>'; + return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content"; + } + + /** + * 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 ""; + } + + /** + * 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()) + { + return static::tag('style', $content, $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()) + { + return static::tag('script', $content, $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['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['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, such as "post", "get", "put", "delete" (case-insensitive). + * Since most browsers only support "post" and "get", if other methods are given, they will + * be simulated using "post", and a hidden input will be added which contains the actual method type. + * See [[\yii\web\Request::restVar]] for more details. + * @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); + + $hiddenInputs = array(); + + $request = Yii::$app->getRequest(); + if ($request instanceof Request) { + if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) { + // simulate PUT, DELETE, etc. via POST + $hiddenInputs[] = static::hiddenInput($request->restVar, $method); + $method = 'post'; + } + if ($request->enableCsrfValidation) { + $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken()); + } + } + + if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { + // query parameters in the action are ignored for GET method + // we use hidden fields to add them back + foreach (explode('&', substr($action, $pos + 1)) as $pair) { + if (($pos1 = strpos($pair, '=')) !== false) { + $hiddenInputs[] = static::hiddenInput( + urldecode(substr($pair, 0, $pos1)), + urldecode(substr($pair, $pos1 + 1)) + ); + } else { + $hiddenInputs[] = static::hiddenInput(urldecode($pair), ''); + } + } + $action = substr($action, 0, $pos); + } + + $options['action'] = $action; + $options['method'] = $method; + $form = static::beginTag('form', $options); + if (!empty($hiddenInputs)) { + $form .= "\n" . implode("\n", $hiddenInputs); + } + + return $form; + } + + /** + * Generates a form end tag. + * @return string the generated tag + * @see beginForm + */ + public static function endForm() + { + return ''; + } + + /** + * 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 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 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()) + { + $options['href'] = 'mailto:' . ($email === null ? $text : $email); + return static::tag('a', $text, $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', '', $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 [[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 $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 button tag + */ + public static function button($content = 'Button', $options = array()) + { + return static::tag('button', $content, $options); + } + + /** + * Generates a submit button tag. + * @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($content = 'Submit', $options = array()) + { + $options['type'] = 'submit'; + return static::button($content, $options); + } + + /** + * Generates a reset button tag. + * @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($content = 'Reset', $options = array()) + { + $options['type'] = 'reset'; + return static::button($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 === null ? null : (string)$value; + return static::tag('input', '', $options); + } + + /** + * Generates an input button. + * @param string $label 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($label = 'Button', $options = array()) + { + $options['type'] = 'button'; + $options['value'] = $label; + return static::tag('input', '', $options); + } + + /** + * Generates a submit input button. + * @param string $label 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($label = 'Submit', $options = array()) + { + $options['type'] = 'submit'; + $options['value'] = $label; + return static::tag('input', '', $options); + } + + /** + * Generates a reset input button. + * @param string $label 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($label = 'Reset', $options = array()) + { + $options['type'] = 'reset'; + $options['value'] = $label; + return static::tag('input', '', $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 array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - 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. + * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the radio button will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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, $options = array()) + { + $options['checked'] = (boolean)$checked; + $value = array_key_exists('value', $options) ? $options['value'] : '1'; + 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 = ''; + } + if (isset($options['label'])) { + $label = $options['label']; + $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); + unset($options['label'], $options['labelOptions']); + $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions); + return $hidden . static::tag('div', $content, array('class' => 'radio')); + } else { + 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 array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - 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. + * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the checkbox will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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, $options = array()) + { + $options['checked'] = (boolean)$checked; + $value = array_key_exists('value', $options) ? $options['value'] : '1'; + 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 = ''; + } + if (isset($options['label'])) { + $label = $options['label']; + $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); + unset($options['label'], $options['labelOptions']); + $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions); + return $hidden . static::tag('div', $content, array('class' => 'checkbox')); + } else { + 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 specially handled: + * + * - 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 specially handled: + * + * - 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 (!empty($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. + * @param array $options options (name => config) for the checkbox list container tag. + * The following options are specially handled: + * + * - tag: string, the tag name of the container element. + * - 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. + * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. + * This option is ignored if `item` option is set. + * - 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, respectively. + * @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; + $encode = !isset($options['encode']) || $options['encode']; + $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::checkbox($name, $checked, array( + 'value' => $value, + 'label' => $encode ? static::encode($label) : $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"; + + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); + + return $hidden . static::tag($tag, implode($separator, $lines), $options); + } + + /** + * 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. + * @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. + * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. + * This option is ignored if `item` option is set. + * - 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, respectively. + * @return string the generated radio button list + */ + public static function radioList($name, $selection = null, $items = array(), $options = array()) + { + $encode = !isset($options['encode']) || $options['encode']; + $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::radio($name, $checked, array( + 'value' => $value, + 'label' => $encode ? static::encode($label) : $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 = ''; + } + + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); + + return $hidden . static::tag($tag, implode($separator, $lines), $options); + } + + /** + * Generates an unordered list. + * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. + * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. + * @param array $options options (name => config) for the radio button list. The following options are supported: + * + * - encode: boolean, whether to HTML-encode the items. Defaults to true. + * This option is ignored if the `item` option is specified. + * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. + * - item: callable, a callback that is used to generate each individual list item. + * The signature of this callback must be: + * + * ~~~ + * function ($item, $index) + * ~~~ + * + * where $index is the array key corresponding to `$item` in `$items`. The callback should return + * the whole list item tag. + * + * @return string the generated unordered list. An empty string is returned if `$items` is empty. + */ + public static function ul($items, $options = array()) + { + if (empty($items)) { + return ''; + } + $tag = isset($options['tag']) ? $options['tag'] : 'ul'; + $encode = !isset($options['encode']) || $options['encode']; + $formatter = isset($options['item']) ? $options['item'] : null; + $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : array(); + unset($options['tag'], $options['encode'], $options['item'], $options['itemOptions']); + $results = array(); + foreach ($items as $index => $item) { + if ($formatter !== null) { + $results[] = call_user_func($formatter, $item, $index); + } else { + $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions); + } + } + return static::tag($tag, "\n" . implode("\n", $results) . "\n", $options); + } + + /** + * Generates an ordered list. + * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. + * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. + * @param array $options options (name => config) for the radio button list. The following options are supported: + * + * - encode: boolean, whether to HTML-encode the items. Defaults to true. + * This option is ignored if the `item` option is specified. + * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. + * - item: callable, a callback that is used to generate each individual list item. + * The signature of this callback must be: + * + * ~~~ + * function ($item, $index) + * ~~~ + * + * where $index is the array key corresponding to `$item` in `$items`. The callback should return + * the whole list item tag. + * + * @return string the generated ordered list. An empty string is returned if `$items` is empty. + */ + public static function ol($items, $options = array()) + { + $options['tag'] = 'ol'; + return static::ul($items, $options); + } + + /** + * Generates a label tag for the given model attribute. + * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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. + * The following options are specially handled: + * + * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]]. + * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display + * (after encoding). + * + * @return string the generated label tag + */ + public static function activeLabel($model, $attribute, $options = array()) + { + $attribute = static::getAttributeName($attribute); + $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute)); + $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute); + unset($options['label'], $options['for']); + return static::label($label, $for, $options); + } + + /** + * Generates a tag that contains the first validation error of the specified model attribute. + * Note that even if there is no validation error, this method will still return an empty error tag. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded + * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. + * + * The following options are specially handled: + * + * - tag: this specifies the tag name. If not set, "div" will be used. + * + * @return string the generated label tag + */ + public static function error($model, $attribute, $options = array()) + { + $attribute = static::getAttributeName($attribute); + $error = $model->getFirstError($attribute); + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag']); + return Html::tag($tag, Html::encode($error), $options); + } + + /** + * Generates an input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param string $type the input type (e.g. 'text', 'password') + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeInput($type, $model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::input($type, $name, $value, $options); + } + + /** + * Generates a text input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeTextInput($model, $attribute, $options = array()) + { + return static::activeInput('text', $model, $attribute, $options); + } + + /** + * Generates a hidden input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeHiddenInput($model, $attribute, $options = array()) + { + return static::activeInput('hidden', $model, $attribute, $options); + } + + /** + * Generates a password input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activePasswordInput($model, $attribute, $options = array()) + { + return static::activeInput('password', $model, $attribute, $options); + } + + /** + * Generates a file input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeFileInput($model, $attribute, $options = array()) + { + // add a hidden field so that if a model only has a file field, we can + // still use isset($_POST[$modelClass]) to detect if the input is submitted + return static::activeHiddenInput($model, $attribute, array('id' => null, 'value' => '')) + . static::activeInput('file', $model, $attribute, $options); + } + + /** + * Generates a textarea tag for the given model attribute. + * The model attribute value will be used as the content in the textarea. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated textarea tag + */ + public static function activeTextarea($model, $attribute, $options = array()) + { + $name = static::getInputName($model, $attribute); + $value = static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::textarea($name, $value, $options); + } + + /** + * Generates a radio button tag for the given model attribute. + * This method will generate the "checked" tag attribute according to the model attribute value. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, + * it will take the default value '0'. This method will render a hidden input 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. + * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the radio button will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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 activeRadio($model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('uncheck', $options)) { + $options['uncheck'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::radio($name, $checked, $options); + } + + /** + * Generates a checkbox tag for the given model attribute. + * This method will generate the "checked" tag attribute according to the model attribute value. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, + * it will take the default value '0'. This method will render a hidden input 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. + * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the checkbox will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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 activeCheckbox($model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('uncheck', $options)) { + $options['uncheck'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::checkbox($name, $checked, $options); + } + + /** + * Generates a drop-down list for the given model attribute. + * The selection of the drop-down list is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeDropDownList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::dropDownList($name, $checked, $items, $options); + } + + /** + * Generates a list box. + * The selection of the list box is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeListBox($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::listBox($name, $checked, $items, $options); + } + + /** + * Generates a list of checkboxes. + * A checkbox list allows multiple selection, like [[listBox()]]. + * As a result, the corresponding submitted value is an array. + * The selection of the checkbox list is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeCheckboxList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::checkboxList($name, $checked, $items, $options); + } + + /** + * Generates a list of radio buttons. + * A radio button list is like a checkbox list, except that it only allows single selection. + * The selection of the radio buttons is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeRadioList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::radioList($name, $checked, $items, $options); + } + + /** + * 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'] = (string)$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. + * Attributes whose values are of boolean type will be treated as + * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes). + * And attributes whose values are null will not be rendered. + * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. + * @return string the rendering result. If the attributes are not empty, they will be rendered + * into a string with a leading white space (so 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 (is_bool($value)) { + if ($value) { + $html .= " $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 first be processed by [[Yii::getAlias()]]. If the result + * is an absolute URL, it will be returned without any change further; Otherwise, the result + * will be prefixed with [[\yii\web\Request::baseUrl]] 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')`. + * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used. + * + * @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 instanceof \yii\web\Controller) { + 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 { + $url = Yii::getAlias($url); + if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) { + return $url; + } else { + return Yii::$app->getRequest()->getBaseUrl() . '/' . $url; + } + } + } + + /** + * Adds a CSS class to the specified options. + * If the CSS class is already in the options, it will not be added again. + * @param array $options the options to be modified. + * @param string $class the CSS class to be added + */ + public static function addCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = ' ' . $options['class'] . ' '; + if (($pos = strpos($classes, ' ' . $class . ' ')) === false) { + $options['class'] .= ' ' . $class; + } + } else { + $options['class'] = $class; + } + } + + /** + * Removes a CSS class from the specified options. + * @param array $options the options to be modified. + * @param string $class the CSS class to be removed + */ + public static function removeCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY)); + if (($index = array_search($class, $classes)) !== false) { + unset($classes[$index]); + } + if (empty($classes)) { + unset($options['class']); + } else { + $options['class'] = implode(' ', $classes); + } + } + } + + /** + * Returns the real attribute name from the given attribute expression. + * + * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. + * It is mainly used in tabular data input and/or input of array type. Below are some examples: + * + * - `[0]content` is used in tabular data input to represent the "content" attribute + * for the first model in tabular input; + * - `dates[0]` represents the first array element of the "dates" attribute; + * - `[0]dates[0]` represents the first array element of the "dates" attribute + * for the first model in tabular input. + * + * If `$attribute` has neither prefix nor suffix, it will be returned back without change. + * @param string $attribute the attribute name or expression + * @return string the attribute name without prefix and suffix. + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getAttributeName($attribute) + { + if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + return $matches[2]; + } else { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + } + + /** + * Returns the value of the specified attribute name or expression. + * + * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`. + * See [[getAttributeName()]] for more details about attribute expression. + * + * @param Model $model the model object + * @param string $attribute the attribute name or expression + * @return mixed the corresponding attribute value + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getAttributeValue($model, $attribute) + { + if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + $attribute = $matches[2]; + $index = $matches[3]; + if ($index === '') { + return $model->$attribute; + } else { + $value = $model->$attribute; + foreach (explode('][', trim($index, '[]')) as $id) { + if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) { + $value = $value[$id]; + } else { + return null; + } + } + return $value; + } + } + + /** + * Generates an appropriate input name for the specified attribute name or expression. + * + * This method generates a name that can be used as the input name to collect user input + * for the specified attribute. The name is generated according to the [[Model::formName|form name]] + * of the model and the given attribute name. For example, if the form name of the `Post` model + * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`. + * + * See [[getAttributeName()]] for explanation of attribute expression. + * + * @param Model $model the model object + * @param string $attribute the attribute name or expression + * @return string the generated input name + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getInputName($model, $attribute) + { + $formName = $model->formName(); + if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + $prefix = $matches[1]; + $attribute = $matches[2]; + $suffix = $matches[3]; + if ($formName === '' && $prefix === '') { + return $attribute . $suffix; + } elseif ($formName !== '') { + return $formName . $prefix . "[$attribute]" . $suffix; + } else { + throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.'); + } + } + + /** + * Generates an appropriate input ID for the specified attribute name or expression. + * + * This method converts the result [[getInputName()]] into a valid input ID. + * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression. + * @return string the generated input ID + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getInputId($model, $attribute) + { + $name = strtolower(static::getInputName($model, $attribute)); + return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name); + } +} diff --git a/framework/yii/helpers/AbstractHtmlPurifier.php b/framework/yii/helpers/AbstractHtmlPurifier.php new file mode 100644 index 0000000..221fc37 --- /dev/null +++ b/framework/yii/helpers/AbstractHtmlPurifier.php @@ -0,0 +1,34 @@ + + * @since 2.0 + */ +abstract class AbstractHtmlPurifier +{ + /** + * Passes markup through HTMLPurifier making it safe to output to end user + * + * @param string $content + * @param array|null $config + * @return string + */ + public static function process($content, $config = null) + { + $configInstance = \HTMLPurifier_Config::create($config); + $configInstance->autoFinalize = false; + $purifier=\HTMLPurifier::instance($configInstance); + $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath()); + return $purifier->purify($content); + } +} diff --git a/framework/yii/helpers/AbstractInflector.php b/framework/yii/helpers/AbstractInflector.php new file mode 100644 index 0000000..27ee4f7 --- /dev/null +++ b/framework/yii/helpers/AbstractInflector.php @@ -0,0 +1,480 @@ + + * @since 2.0 + */ +abstract class AbstractInflector +{ + /** + * @var array the rules for converting a word into its plural form. + * The keys are the regular expressions and the values are the corresponding replacements. + */ + public static $plurals = array( + '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1', + '/^(sea[- ]bass)$/i' => '\1', + '/(m)ove$/i' => '\1oves', + '/(f)oot$/i' => '\1eet', + '/(h)uman$/i' => '\1umans', + '/(s)tatus$/i' => '\1tatuses', + '/(s)taff$/i' => '\1taff', + '/(t)ooth$/i' => '\1eeth', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ); + /** + * @var array the rules for converting a word into its singular form. + * The keys are the regular expressions and the values are the corresponding replacements. + */ + public static $singulars = array( + '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1', + '/^(sea[- ]bass)$/i' => '\1', + '/(s)tatuses$/i' => '\1tatus', + '/(f)eet$/i' => '\1oot', + '/(t)eeth$/i' => '\1ooth', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '', + ); + /** + * @var array the special rules for converting a word between its plural form and singular form. + * The keys are the special words in singular form, and the values are the corresponding plural form. + */ + public static $specials = array( + 'atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'curve' => 'curves', + 'foe' => 'foes', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs', + 'wave' => 'waves', + 'Amoyese' => 'Amoyese', + 'bison' => 'bison', + 'Borghese' => 'Borghese', + 'bream' => 'bream', + 'breeches' => 'breeches', + 'britches' => 'britches', + 'buffalo' => 'buffalo', + 'cantus' => 'cantus', + 'carp' => 'carp', + 'chassis' => 'chassis', + 'clippers' => 'clippers', + 'cod' => 'cod', + 'coitus' => 'coitus', + 'Congoese' => 'Congoese', + 'contretemps' => 'contretemps', + 'corps' => 'corps', + 'debris' => 'debris', + 'diabetes' => 'diabetes', + 'djinn' => 'djinn', + 'eland' => 'eland', + 'elk' => 'elk', + 'equipment' => 'equipment', + 'Faroese' => 'Faroese', + 'flounder' => 'flounder', + 'Foochowese' => 'Foochowese', + 'gallows' => 'gallows', + 'Genevese' => 'Genevese', + 'Genoese' => 'Genoese', + 'Gilbertese' => 'Gilbertese', + 'graffiti' => 'graffiti', + 'headquarters' => 'headquarters', + 'herpes' => 'herpes', + 'hijinks' => 'hijinks', + 'Hottentotese' => 'Hottentotese', + 'information' => 'information', + 'innings' => 'innings', + 'jackanapes' => 'jackanapes', + 'Kiplingese' => 'Kiplingese', + 'Kongoese' => 'Kongoese', + 'Lucchese' => 'Lucchese', + 'mackerel' => 'mackerel', + 'Maltese' => 'Maltese', + 'mews' => 'mews', + 'moose' => 'moose', + 'mumps' => 'mumps', + 'Nankingese' => 'Nankingese', + 'news' => 'news', + 'nexus' => 'nexus', + 'Niasese' => 'Niasese', + 'Pekingese' => 'Pekingese', + 'Piedmontese' => 'Piedmontese', + 'pincers' => 'pincers', + 'Pistoiese' => 'Pistoiese', + 'pliers' => 'pliers', + 'Portuguese' => 'Portuguese', + 'proceedings' => 'proceedings', + 'rabies' => 'rabies', + 'rice' => 'rice', + 'rhinoceros' => 'rhinoceros', + 'salmon' => 'salmon', + 'Sarawakese' => 'Sarawakese', + 'scissors' => 'scissors', + 'series' => 'series', + 'Shavese' => 'Shavese', + 'shears' => 'shears', + 'siemens' => 'siemens', + 'species' => 'species', + 'swine' => 'swine', + 'testes' => 'testes', + 'trousers' => 'trousers', + 'trout' => 'trout', + 'tuna' => 'tuna', + 'Vermontese' => 'Vermontese', + 'Wenchowese' => 'Wenchowese', + 'whiting' => 'whiting', + 'wildebeest' => 'wildebeest', + 'Yengeese' => 'Yengeese', + ); + /** + * @var array map of special chars and its translation. This is used by [[slug()]]. + */ + public static $transliteration = array( + '/ä|æ|ǽ/' => 'ae', + '/ö|œ/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', + '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', + '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', + '/ç|ć|ĉ|ċ|č/' => 'c', + '/Ð|Ď|Đ/' => 'D', + '/ð|ď|đ/' => 'd', + '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E', + '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e', + '/Ĝ|Ğ|Ġ|Ģ/' => 'G', + '/ĝ|ğ|ġ|ģ/' => 'g', + '/Ĥ|Ħ/' => 'H', + '/ĥ|ħ/' => 'h', + '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I', + '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i', + '/Ĵ/' => 'J', + '/ĵ/' => 'j', + '/Ķ/' => 'K', + '/ķ/' => 'k', + '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L', + '/ĺ|ļ|ľ|ŀ|ł/' => 'l', + '/Ñ|Ń|Ņ|Ň/' => 'N', + '/ñ|ń|ņ|ň|ʼn/' => 'n', + '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O', + '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o', + '/Ŕ|Ŗ|Ř/' => 'R', + '/ŕ|ŗ|ř/' => 'r', + '/Ś|Ŝ|Ş|Ș|Š/' => 'S', + '/ś|ŝ|ş|ș|š|ſ/' => 's', + '/Ţ|Ț|Ť|Ŧ/' => 'T', + '/ţ|ț|ť|ŧ/' => 't', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U', + '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u', + '/Ý|Ÿ|Ŷ/' => 'Y', + '/ý|ÿ|ŷ/' => 'y', + '/Ŵ/' => 'W', + '/ŵ/' => 'w', + '/Ź|Ż|Ž/' => 'Z', + '/ź|ż|ž/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/' => 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Œ/' => 'OE', + '/ƒ/' => 'f' + ); + + /** + * 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 $word the word to be pluralized + * @return string the pluralized word + */ + public static function pluralize($word) + { + if (isset(self::$specials[$word])) { + return self::$specials[$word]; + } + foreach (static::$plurals as $rule => $replacement) { + if (preg_match($rule, $word)) { + return preg_replace($rule, $replacement, $word); + } + } + return $word; + } + + /** + * Returns the singular of the $word + * @param string $word the english word to singularize + * @return string Singular noun. + */ + public static function singularize($word) + { + $result = array_search($word, self::$specials, true); + if ($result !== false) { + return $result; + } + foreach (static::$singulars as $rule => $replacement) { + if (preg_match($rule, $word)) { + return preg_replace($rule, $replacement, $word); + } + } + return $word; + } + + /** + * Converts an underscored or CamelCase word into a English + * sentence. + * @param string $words + * @param bool $ucAll whether to set all words to uppercase + * @return string + */ + public static function titleize($words, $ucAll = false) + { + $words = static::humanize(static::underscore($words), $ucAll); + return $ucAll ? ucwords($words) : ucfirst($words); + } + + /** + * Returns given word as CamelCased + * Converts a word like "send_email" to "SendEmail". It + * will remove non alphanumeric character from the word, so + * "who's online" will be converted to "WhoSOnline" + * @see variablize + * @param string $word the word to CamelCase + * @return string + */ + public static function camelize($word) + { + return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); + } + + /** + * 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('/(? ' ', + '/\\s+/' => $replacement, + '/(?<=[a-z])([A-Z])/' => $replacement . '\\1', + str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => '' + ); + return preg_replace(array_keys($map), array_values($map), $string); + } + + /** + * Converts a table name to its class name. For example, converts "people" to "Person" + * @param string $tableName + * @return string + */ + public static function classify($tableName) + { + return static::camelize(static::singularize($tableName)); + } + + /** + * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ... + * @param int $number the number to get its ordinal value + * @return string + */ + public static function ordinalize($number) + { + if (in_array(($number % 100), range(11, 13))) { + return $number . 'th'; + } + switch ($number % 10) { + case 1: return $number . 'st'; + case 2: return $number . 'nd'; + case 3: return $number . 'rd'; + default: return $number . 'th'; + } + } +} diff --git a/framework/yii/helpers/AbstractJson.php b/framework/yii/helpers/AbstractJson.php new file mode 100644 index 0000000..cda71a0 --- /dev/null +++ b/framework/yii/helpers/AbstractJson.php @@ -0,0 +1,112 @@ + + * @since 2.0 + */ +abstract class AbstractJson +{ + /** + * Encodes the given value into a JSON string. + * The method enhances `json_encode()` by supporting JavaScript expressions. + * In particular, the method will not encode a JavaScript expression that is + * represented in terms of a [[JsExpression]] object. + * @param mixed $value the data to be encoded + * @param integer $options the encoding options. For more details please refer to + * [[http://www.php.net/manual/en/function.json-encode.php]] + * @return string the encoding result + */ + public static function encode($value, $options = 0) + { + $expressions = array(); + $value = static::processData($value, $expressions, uniqid()); + $json = json_encode($value, $options); + return empty($expressions) ? $json : strtr($json, $expressions); + } + + /** + * Decodes the given JSON string into a PHP data structure. + * @param string $json the JSON string to be decoded + * @param boolean $asArray whether to return objects in terms of associative arrays. + * @return mixed the PHP data + * @throws InvalidParamException if there is any decoding error + */ + public static function decode($json, $asArray = true) + { + if (is_array($json)) { + throw new InvalidParamException('Invalid JSON data.'); + } + $decode = json_decode((string)$json, $asArray); + switch (json_last_error()) { + case JSON_ERROR_NONE: + break; + case JSON_ERROR_DEPTH: + throw new InvalidParamException('The maximum stack depth has been exceeded.'); + case JSON_ERROR_CTRL_CHAR: + throw new InvalidParamException('Control character error, possibly incorrectly encoded.'); + case JSON_ERROR_SYNTAX: + throw new InvalidParamException('Syntax error.'); + case JSON_ERROR_STATE_MISMATCH: + throw new InvalidParamException('Invalid or malformed JSON.'); + case JSON_ERROR_UTF8: + throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.'); + default: + throw new InvalidParamException('Unknown JSON decoding error.'); + } + + return $decode; + } + + /** + * Pre-processes the data before sending it to `json_encode()`. + * @param mixed $data the data to be processed + * @param array $expressions collection of JavaScript expressions + * @param string $expPrefix a prefix internally used to handle JS expressions + * @return mixed the processed data + */ + protected static function processData($data, &$expressions, $expPrefix) + { + if (is_array($data)) { + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $data[$key] = static::processData($value, $expressions, $expPrefix); + } + } + return $data; + } elseif (is_object($data)) { + if ($data instanceof JsExpression) { + $token = "!{[$expPrefix=" . count($expressions) . ']}!'; + $expressions['"' . $token . '"'] = $data->expression; + return $token; + } else { + $data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data); + $result = array(); + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $result[$key] = static::processData($value, $expressions, $expPrefix); + } else { + $result[$key] = $value; + } + } + return $result; + } + } else { + return $data; + } + } +} diff --git a/framework/yii/helpers/AbstractMarkdown.php b/framework/yii/helpers/AbstractMarkdown.php new file mode 100644 index 0000000..6b76b44 --- /dev/null +++ b/framework/yii/helpers/AbstractMarkdown.php @@ -0,0 +1,44 @@ + + * @since 2.0 + */ +abstract class AbstractMarkdown +{ + /** + * @var MarkdownExtra + */ + protected static $markdown; + + /** + * Converts markdown into HTML + * + * @param string $content + * @param array $config + * @return string + */ + public static function process($content, $config = array()) + { + if (static::$markdown === null) { + static::$markdown = new MarkdownExtra(); + } + foreach ($config as $name => $value) { + static::$markdown->{$name} = $value; + } + return static::$markdown->transform($content); + } +} diff --git a/framework/yii/helpers/AbstractSecurity.php b/framework/yii/helpers/AbstractSecurity.php new file mode 100644 index 0000000..d308b7f --- /dev/null +++ b/framework/yii/helpers/AbstractSecurity.php @@ -0,0 +1,285 @@ + + * @author Tom Worster + * @since 2.0 + */ +abstract class AbstractSecurity +{ + /** + * 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(); + // 192-bit (24 bytes) key size + $key = StringHelper::substr($key, 0, 24); + 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(); + // 192-bit (24 bytes) key size + $key = StringHelper::substr($key, 0, 24); + $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 = array(); + if (is_file($keyFile)) { + $keys = require($keyFile); + } + } + if (!isset($keys[$name])) { + $keys[$name] = static::generateRandomKey($length); + file_put_contents($keyFile, " 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 > 31) { + 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; + } +} diff --git a/framework/yii/helpers/AbstractStringHelper.php b/framework/yii/helpers/AbstractStringHelper.php new file mode 100644 index 0000000..050481e --- /dev/null +++ b/framework/yii/helpers/AbstractStringHelper.php @@ -0,0 +1,138 @@ + + * @author Alex Makarov + * @since 2.0 + */ +abstract class AbstractStringHelper +{ + /** + * Returns the number of bytes in the given string. + * This method ensures the string is treated as a byte array by using `mb_strlen()`. + * @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 mb_strlen($string, '8bit'); + } + + /** + * Returns the portion of string specified by the start and length parameters. + * This method ensures the string is treated as a byte array by using `mb_substr()`. + * @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 mb_substr($string, $start, $length, '8bit'); + } + + /** + * Returns the trailing name component of a path. + * This method is similar to the php function `basename()` except that it will + * treat both \ and / as directory separators, independent of the operating system. + * This method was mainly created to work on php namespaces. When working with real + * file paths, php's `basename()` should work fine for you. + * Note: this method is not aware of the actual filesystem, or path components such as "..". + * @param string $path A path string. + * @param string $suffix If the name component ends in suffix this will also be cut off. + * @return string the trailing name component of the given path. + * @see http://www.php.net/manual/en/function.basename.php + */ + public static function basename($path, $suffix = '') + { + if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) { + $path = mb_substr($path, 0, -$len); + } + $path = rtrim(str_replace('\\', '/', $path), '/\\'); + if (($pos = mb_strrpos($path, '/')) !== false) { + return mb_substr($path, $pos + 1); + } + return $path; + } + + /** + * Returns parent directory's path. + * This method is similar to `dirname()` except that it will treat + * both \ and / as directory separators, independent of the operating system. + * @param string $path A path string. + * @return string the parent directory's path. + * @see http://www.php.net/manual/en/function.basename.php + */ + public static function dirname($path) + { + $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); + if ($pos !== false) { + return mb_substr($path, 0, $pos); + } else { + return $path; + } + } + + /** + * Compares two strings or string arrays, and return their differences. + * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package. + * @param string|array $lines1 the first string or string array to be compared. If it is a string, + * it will be converted into a string array by breaking at newlines. + * @param string|array $lines2 the second string or string array to be compared. If it is a string, + * it will be converted into a string array by breaking at newlines. + * @param string $format the output format. It must be 'inline', 'unified', 'context', 'side-by-side', or 'array'. + * @return string|array the comparison result. An array is returned if `$format` is 'array'. For all other + * formats, a string is returned. + * @throws InvalidParamException if the format is invalid. + */ + public static function diff($lines1, $lines2, $format = 'inline') + { + if (!is_array($lines1)) { + $lines1 = explode("\n", $lines1); + } + if (!is_array($lines2)) { + $lines2 = explode("\n", $lines2); + } + foreach ($lines1 as $i => $line) { + $lines1[$i] = rtrim($line, "\r\n"); + } + foreach ($lines2 as $i => $line) { + $lines2[$i] = rtrim($line, "\r\n"); + } + switch ($format) { + case 'inline': + $renderer = new \Diff_Renderer_Html_Inline(); + break; + case 'array': + $renderer = new \Diff_Renderer_Html_Array(); + break; + case 'side-by-side': + $renderer = new \Diff_Renderer_Html_SideBySide(); + break; + case 'context': + $renderer = new \Diff_Renderer_Text_Context(); + break; + case 'unified': + $renderer = new \Diff_Renderer_Text_Unified(); + break; + default: + throw new InvalidParamException("Output format must be 'inline', 'side-by-side', 'array', 'context' or 'unified'."); + } + $diff = new \Diff($lines1, $lines2); + return $diff->render($renderer); + } +} diff --git a/framework/yii/helpers/AbstractVarDumper.php b/framework/yii/helpers/AbstractVarDumper.php new file mode 100644 index 0000000..2c9f194 --- /dev/null +++ b/framework/yii/helpers/AbstractVarDumper.php @@ -0,0 +1,127 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\helpers; + +/** + * AbstractVarDumper provides concrete implementation for [[VarDumper]]. + * + * Do not use AbstractVarDumper. Use [[VarDumper]] instead. + * + * @author Qiang Xue + * @since 2.0 + */ +abstract class AbstractVarDumper +{ + 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 static::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("/', '', $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 = array_push(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; + } + } +} diff --git a/framework/yii/helpers/ArrayHelper.php b/framework/yii/helpers/ArrayHelper.php index 085104b..a63d3c2 100644 --- a/framework/yii/helpers/ArrayHelper.php +++ b/framework/yii/helpers/ArrayHelper.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class ArrayHelper extends ArrayHelperBase +class ArrayHelper extends AbstractArrayHelper { } diff --git a/framework/yii/helpers/ArrayHelperBase.php b/framework/yii/helpers/ArrayHelperBase.php deleted file mode 100644 index 59129de..0000000 --- a/framework/yii/helpers/ArrayHelperBase.php +++ /dev/null @@ -1,451 +0,0 @@ - - * @since 2.0 - */ -class ArrayHelperBase -{ - /** - * Converts an object or an array of objects into an array. - * @param object|array $object the object to be converted into an array - * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays. - * The properties specified for each class is an array of the following format: - * - * ~~~ - * array( - * 'app\models\Post' => array( - * 'id', - * 'title', - * // the key name in array result => property name - * 'createTime' => 'create_time', - * // the key name in array result => anonymous function - * 'length' => function ($post) { - * return strlen($post->content); - * }, - * ), - * ) - * ~~~ - * - * The result of `ArrayHelper::toArray($post, $properties)` could be like the following: - * - * ~~~ - * array( - * 'id' => 123, - * 'title' => 'test', - * 'createTime' => '2013-01-01 12:00AM', - * 'length' => 301, - * ) - * ~~~ - * - * @param boolean $recursive whether to recursively converts properties which are objects into arrays. - * @return array the array representation of the object - */ - public static function toArray($object, $properties = array(), $recursive = true) - { - if (!empty($properties) && is_object($object)) { - $className = get_class($object); - if (!empty($properties[$className])) { - $result = array(); - foreach ($properties[$className] as $key => $name) { - if (is_int($key)) { - $result[$name] = $object->$name; - } else { - $result[$key] = static::getValue($object, $name); - } - } - return $result; - } - } - if ($object instanceof Arrayable) { - $object = $object->toArray(); - if (!$recursive) { - return $object; - } - } - $result = array(); - foreach ($object as $key => $value) { - if ($recursive && (is_array($value) || is_object($value))) { - $result[$key] = static::toArray($value, true); - } else { - $result[$key] = $value; - } - } - return $result; - } - - /** - * 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 (!empty($args)) { - $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 element if found, default value otherwise - */ - 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; - } - } - - /** - * Removes an item from an array and returns the value. If the key does not exist in the array, the default value - * will be returned instead. - * - * Usage examples, - * - * ~~~ - * // $array = array('type' => 'A', 'options' => array(1, 2)); - * // working with array - * $type = \yii\helpers\ArrayHelper::remove($array, 'type'); - * // $array content - * // $array = array('options' => array(1, 2)); - * ~~~ - * - * @param array $array the array to extract value from - * @param string $key key name of the array element - * @param mixed $default the default value to be returned if the specified key does not exist - * @return mixed|null the value of the element if found, default value otherwise - */ - public static function remove(&$array, $key, $default = null) - { - if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { - $value = $array[$key]; - unset($array[$key]); - return $value; - } - return $default; - } - - /** - * 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 $descending whether to sort in descending or ascending order. When - * sorting by multiple keys with different descending orders, use an array of descending flags. - * @param integer|array $sortFlag the PHP sort flag. Valid values include - * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`. - * Please refer to [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. - * @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter - * is used only when `$sortFlag` is `SORT_STRING`. - * When sorting by multiple keys with different case sensitivities, use an array of boolean values. - * @throws InvalidParamException if the $descending or $sortFlag parameters do not have - * correct number of elements as that of $key. - */ - public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true) - { - $keys = is_array($key) ? $key : array($key); - if (empty($keys) || empty($array)) { - return; - } - $n = count($keys); - if (is_scalar($descending)) { - $descending = array_fill(0, $n, $descending); - } elseif (count($descending) !== $n) { - throw new InvalidParamException('The length of $descending 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 $sortFlag parameter must be the same as that of $keys.'); - } - if (is_scalar($caseSensitive)) { - $caseSensitive = array_fill(0, $n, $caseSensitive); - } elseif (count($caseSensitive) !== $n) { - throw new InvalidParamException('The length of $caseSensitive parameter must be the same as that of $keys.'); - } - $args = array(); - foreach ($keys as $i => $key) { - $flag = $sortFlag[$i]; - $cs = $caseSensitive[$i]; - if (!$cs && ($flag === SORT_STRING)) { - if (defined('SORT_FLAG_CASE')) { - $flag = $flag | SORT_FLAG_CASE; - $args[] = static::getColumn($array, $key); - } else { - $column = array(); - foreach (static::getColumn($array, $key) as $k => $value) { - $column[$k] = mb_strtolower($value); - } - $args[] = $column; - } - } else { - $args[] = static::getColumn($array, $key); - } - $args[] = $descending[$i] ? SORT_DESC : SORT_ASC; - $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; - } -} diff --git a/framework/yii/helpers/Console.php b/framework/yii/helpers/Console.php index c0b7b32..9b0656e 100644 --- a/framework/yii/helpers/Console.php +++ b/framework/yii/helpers/Console.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Carsten Brandt * @since 2.0 */ -class Console extends ConsoleBase +class Console extends AbstractConsole { } diff --git a/framework/yii/helpers/ConsoleBase.php b/framework/yii/helpers/ConsoleBase.php deleted file mode 100644 index a985291..0000000 --- a/framework/yii/helpers/ConsoleBase.php +++ /dev/null @@ -1,835 +0,0 @@ - - * @since 2.0 - */ -class ConsoleBase -{ - 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 RESET = 0; - const NORMAL = 0; - 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"; - } - - /** - * Returns the ANSI format code. - * - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @return string The ANSI format code according to the given formatting constants. - */ - public static function ansiFormatCode($format) - { - return "\033[" . implode(';', $format) . 'm'; - } - - /** - * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards. - * - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @see ansiFormatCode() - * @see ansiFormatEnd() - */ - public static function beginAnsiFormat($format) - { - echo "\033[" . implode(';', $format) . 'm'; - } - - /** - * Resets any ANSI format set by previous method [[ansiFormatBegin()]] - * Any output after this will have default text format. - * This is equal to calling - * - * ```php - * echo Console::ansiFormatCode(array(Console::RESET)) - * ``` - */ - public static function endAnsiFormat() - { - echo "\033[0m"; - } - - /** - * Will return a string formatted with the given ANSI style - * - * @param string $string the string to be formatted - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @return string - */ - public static function ansiFormat($string, $format = array()) - { - $code = implode(';', $format); - return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; - } - - /** - * Returns the ansi format code for xterm foreground color. - * You can pass the return value of this to one of the formatting methods: - * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] - * - * @param integer $colorCode xterm color code - * @return string - * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors - */ - public static function xtermFgColor($colorCode) - { - return '38;5;' . $colorCode; - } - - /** - * Returns the ansi format code for xterm background color. - * You can pass the return value of this to one of the formatting methods: - * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] - * - * @param integer $colorCode xterm color code - * @return string - * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors - */ - public static function xtermBgColor($colorCode) - { - return '48;5;' . $colorCode; - } - - /** - * Strips ANSI control codes from a string - * - * @param string $string String to strip - * @return string - */ - public static function stripAnsiFormat($string) - { - return preg_replace('/\033\[[\d;?]*\w/', '', $string); - } - - /** - * Converts an ANSI formatted string to HTML - * @param $string - * @return mixed - */ - // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 - 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 self::FG_BLACK: - $style = array('color' => '#000000'); - break; - case self::FG_BLUE: - $style = array('color' => '#000078'); - break; - case self::FG_CYAN: - $style = array('color' => '#007878'); - break; - case self::FG_GREEN: - $style = array('color' => '#007800'); - break; - case self::FG_GREY: - $style = array('color' => '#787878'); - break; - case self::FG_PURPLE: - $style = array('color' => '#780078'); - break; - case self::FG_RED: - $style = array('color' => '#780000'); - break; - case self::FG_YELLOW: - $style = array('color' => '#787800'); - break; - case self::BG_BLACK: - $style = array('background-color' => '#000000'); - break; - case self::BG_BLUE: - $style = array('background-color' => '#000078'); - break; - case self::BG_CYAN: - $style = array('background-color' => '#007878'); - break; - case self::BG_GREEN: - $style = array('background-color' => '#007800'); - break; - case self::BG_GREY: - $style = array('background-color' => '#787878'); - break; - case self::BG_PURPLE: - $style = array('background-color' => '#780078'); - break; - case self::BG_RED: - $style = array('background-color' => '#780000'); - break; - case self::BG_YELLOW: - $style = array('background-color' => '#787800'); - break; - case self::BOLD: - $style = array('font-weight' => 'bold'); - break; - case self::ITALIC: - $style = array('font-style' => 'italic'); - break; - case self::UNDERLINE: - $style = array('text-decoration' => array('underline')); - break; - case self::OVERLINED: - $style = array('text-decoration' => array('overline')); - break; - case self::CROSSED_OUT: - $style = array('text-decoration' => array('line-through')); - break; - case self::BLINK: - $style = array('text-decoration' => array('blink')); - break; - case self::NEGATIVE: // ??? - case self::CONCEALED: - case self::ENCIRCLED: - case self::FRAMED: - // TODO allow resetting codes - break; - case 0: // ansi reset - $return = ''; - for ($n = $tags; $tags > 0; $tags--) { - $return .= ''; - } - 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 ' $value) { - echo " $key - $value\n"; - } - echo " ? - Show help\n"; - goto top; - } elseif (!in_array($input, array_keys($options))) { - goto top; - } - return $input; - } - - /** - * Displays and updates a simple progress bar on screen. - * - * @param integer $done the number of items that are completed - * @param integer $total the total value of items that are to be done - * @param integer $size the size of the status bar (optional) - * @see http://snipplr.com/view/29548/ - */ - public static function showProgress($done, $total, $size = 30) - { - static $start; - - // if we go over our bound, just ignore it - if ($done > $total) { - return; - } - - if (empty($start)) { - $start = time(); - } - - $now = time(); - - $percent = (double)($done / $total); - $bar = floor($percent * $size); - - $status = "\r["; - $status .= str_repeat("=", $bar); - if ($bar < $size) { - $status .= ">"; - $status .= str_repeat(" ", $size - $bar); - } else { - $status .= "="; - } - - $display = number_format($percent * 100, 0); - - $status .= "] $display% $done/$total"; - - $rate = ($now - $start) / $done; - $left = $total - $done; - $eta = round($rate * $left, 2); - - $elapsed = $now - $start; - - $status .= " remaining: " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec."; - - static::stdout("$status "); - - flush(); - - // when done, send a newline - if ($done == $total) { - echo "\n"; - } - } -} diff --git a/framework/yii/helpers/FileHelper.php b/framework/yii/helpers/FileHelper.php index 9241025..919dc09 100644 --- a/framework/yii/helpers/FileHelper.php +++ b/framework/yii/helpers/FileHelper.php @@ -16,6 +16,6 @@ namespace yii\helpers; * @author Alex Makarov * @since 2.0 */ -class FileHelper extends FileHelperBase +class FileHelper extends AbstractFileHelper { } diff --git a/framework/yii/helpers/FileHelperBase.php b/framework/yii/helpers/FileHelperBase.php deleted file mode 100644 index 0e480da..0000000 --- a/framework/yii/helpers/FileHelperBase.php +++ /dev/null @@ -1,329 +0,0 @@ - - * @author Alex Makarov - * @since 2.0 - */ -class FileHelperBase -{ - /** - * 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 the 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. - * - * @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); - finfo_close($info); - if ($result !== false) { - return $result; - } - } - } - - return $checkExtension ? static::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) - { - static $mimeTypes = array(); - if ($magicFile === null) { - $magicFile = __DIR__ . '/mimeTypes.php'; - } - if (!isset($mimeTypes[$magicFile])) { - $mimeTypes[$magicFile] = require($magicFile); - } - if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { - $ext = strtolower($ext); - if (isset($mimeTypes[$magicFile][$ext])) { - return $mimeTypes[$magicFile][$ext]; - } - } - return null; - } - - /** - * Copies a whole directory as another one. - * The files and sub-directories will also be copied over. - * @param string $src the source directory - * @param string $dst the destination directory - * @param array $options options for directory copy. Valid options are: - * - * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775. - * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. - * - filter: callback, a PHP callback that is called for each directory or file. - * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. - * The callback can return one of the following values: - * - * * true: the directory or file will be copied (the "only" and "except" options will be ignored) - * * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored) - * * null: the "only" and "except" options will determine whether the directory or file should be copied - * - * - only: array, list of patterns that the file paths should match if they want to be copied. - * A path matches a pattern if it contains the pattern string at its end. - * For example, '.php' matches all file paths ending with '.php'. - * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. - * If a file path matches a pattern in both "only" and "except", it will NOT be copied. - * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied. - * A path matches a pattern if it contains the pattern string at its end. - * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' - * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; - * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches - * both '/' and '\' in the paths. - * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true. - * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied. - * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or - * file copied from, while `$to` is the copy target. - */ - public static function copyDirectory($src, $dst, $options = array()) - { - if (!is_dir($dst)) { - static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true); - } - - $handle = opendir($src); - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $from = $src . DIRECTORY_SEPARATOR . $file; - $to = $dst . DIRECTORY_SEPARATOR . $file; - if (static::filterPath($from, $options)) { - if (is_file($from)) { - copy($from, $to); - if (isset($options['fileMode'])) { - @chmod($to, $options['fileMode']); - } - } else { - static::copyDirectory($from, $to, $options); - } - if (isset($options['afterCopy'])) { - call_user_func($options['afterCopy'], $from, $to); - } - } - } - closedir($handle); - } - - /** - * Removes a directory (and all its content) recursively. - * @param string $dir the directory to be deleted recursively. - */ - public static function removeDirectory($dir) - { - if (!is_dir($dir) || !($handle = opendir($dir))) { - return; - } - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (is_file($path)) { - unlink($path); - } else { - static::removeDirectory($path); - } - } - closedir($handle); - rmdir($dir); - } - - /** - * Returns the files found under the specified directory and subdirectories. - * @param string $dir the directory under which the files will be looked for. - * @param array $options options for file searching. Valid options are: - * - * - filter: callback, a PHP callback that is called for each directory or file. - * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. - * The callback can return one of the following values: - * - * * true: the directory or file will be returned (the "only" and "except" options will be ignored) - * * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored) - * * null: the "only" and "except" options will determine whether the directory or file should be returned - * - * - only: array, list of patterns that the file paths should match if they want to be returned. - * A path matches a pattern if it contains the pattern string at its end. - * For example, '.php' matches all file paths ending with '.php'. - * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. - * If a file path matches a pattern in both "only" and "except", it will NOT be returned. - * - except: array, list of patterns that the file paths or directory paths should match if they want to be excluded from the result. - * A path matches a pattern if it contains the pattern string at its end. - * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' - * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; - * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches - * both '/' and '\' in the paths. - * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. - * @return array files found under the directory. The file list is sorted. - */ - public static function findFiles($dir, $options = array()) - { - $list = array(); - $handle = opendir($dir); - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (static::filterPath($path, $options)) { - if (is_file($path)) { - $list[] = $path; - } elseif (!isset($options['recursive']) || $options['recursive']) { - $list = array_merge($list, static::findFiles($path, $options)); - } - } - } - closedir($handle); - return $list; - } - - /** - * Checks if the given file path satisfies the filtering options. - * @param string $path the path of the file or directory to be checked - * @param array $options the filtering options. See [[findFiles()]] for explanations of - * the supported options. - * @return boolean whether the file or directory satisfies the filtering options. - */ - public static function filterPath($path, $options) - { - if (isset($options['filter'])) { - $result = call_user_func($options['filter'], $path); - if (is_bool($result)) { - return $result; - } - } - $path = str_replace('\\', '/', $path); - if ($isDir = is_dir($path)) { - $path .= '/'; - } - $n = StringHelper::strlen($path); - - if (!empty($options['except'])) { - foreach ($options['except'] as $name) { - if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { - return false; - } - } - } - - if (!$isDir && !empty($options['only'])) { - foreach ($options['only'] as $name) { - if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { - return true; - } - } - return false; - } - return true; - } - - /** - * Creates a new directory. - * - * This method is similar to the PHP `mkdir()` function except that - * it uses `chmod()` to set the permission of the created directory - * in order to avoid the impact of the `umask` setting. - * - * @param string $path path of the directory to be created. - * @param integer $mode the permission to be set for the created directory. - * @param boolean $recursive whether to create parent directories if they do not exist. - * @return boolean whether the directory is created successfully - */ - public static function createDirectory($path, $mode = 0775, $recursive = true) - { - if (is_dir($path)) { - return true; - } - $parentDir = dirname($path); - if ($recursive && !is_dir($parentDir)) { - static::createDirectory($parentDir, $mode, true); - } - $result = mkdir($path, $mode); - chmod($path, $mode); - return $result; - } -} diff --git a/framework/yii/helpers/Html.php b/framework/yii/helpers/Html.php index 8e4f1c9..0715c6c 100644 --- a/framework/yii/helpers/Html.php +++ b/framework/yii/helpers/Html.php @@ -13,6 +13,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class Html extends HtmlBase +class Html extends AbstractHtml { } diff --git a/framework/yii/helpers/HtmlBase.php b/framework/yii/helpers/HtmlBase.php deleted file mode 100644 index a5786cb..0000000 --- a/framework/yii/helpers/HtmlBase.php +++ /dev/null @@ -1,1599 +0,0 @@ - - * @since 2.0 - */ -class HtmlBase -{ - /** - * @var array list of void elements (element name => 1) - * @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 array the preferred order of attributes in a tag. This mainly affects the order of the attributes - * that are rendered by [[renderTagAttributes()]]. - */ - 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 - * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false, - * HTML entities in `$content` will not be further encoded. - * @return string the encoded content - * @see decode - * @see http://www.php.net/manual/en/function.htmlspecialchars.php - */ - public static function encode($content, $doubleEncode = true) - { - return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode); - } - - /** - * 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) . '>'; - return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content"; - } - - /** - * 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 ""; - } - - /** - * 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()) - { - return static::tag('style', $content, $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()) - { - return static::tag('script', $content, $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['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['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, such as "post", "get", "put", "delete" (case-insensitive). - * Since most browsers only support "post" and "get", if other methods are given, they will - * be simulated using "post", and a hidden input will be added which contains the actual method type. - * See [[\yii\web\Request::restVar]] for more details. - * @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); - - $hiddenInputs = array(); - - $request = Yii::$app->getRequest(); - if ($request instanceof Request) { - if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) { - // simulate PUT, DELETE, etc. via POST - $hiddenInputs[] = static::hiddenInput($request->restVar, $method); - $method = 'post'; - } - if ($request->enableCsrfValidation) { - $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken()); - } - } - - if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { - // query parameters in the action are ignored for GET method - // we use hidden fields to add them back - foreach (explode('&', substr($action, $pos + 1)) as $pair) { - if (($pos1 = strpos($pair, '=')) !== false) { - $hiddenInputs[] = static::hiddenInput( - urldecode(substr($pair, 0, $pos1)), - urldecode(substr($pair, $pos1 + 1)) - ); - } else { - $hiddenInputs[] = static::hiddenInput(urldecode($pair), ''); - } - } - $action = substr($action, 0, $pos); - } - - $options['action'] = $action; - $options['method'] = $method; - $form = static::beginTag('form', $options); - if (!empty($hiddenInputs)) { - $form .= "\n" . implode("\n", $hiddenInputs); - } - - return $form; - } - - /** - * Generates a form end tag. - * @return string the generated tag - * @see beginForm - */ - public static function endForm() - { - return ''; - } - - /** - * 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 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 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()) - { - $options['href'] = 'mailto:' . ($email === null ? $text : $email); - return static::tag('a', $text, $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', '', $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 [[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 $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 button tag - */ - public static function button($content = 'Button', $options = array()) - { - return static::tag('button', $content, $options); - } - - /** - * Generates a submit button tag. - * @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($content = 'Submit', $options = array()) - { - $options['type'] = 'submit'; - return static::button($content, $options); - } - - /** - * Generates a reset button tag. - * @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($content = 'Reset', $options = array()) - { - $options['type'] = 'reset'; - return static::button($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 === null ? null : (string)$value; - return static::tag('input', '', $options); - } - - /** - * Generates an input button. - * @param string $label 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($label = 'Button', $options = array()) - { - $options['type'] = 'button'; - $options['value'] = $label; - return static::tag('input', '', $options); - } - - /** - * Generates a submit input button. - * @param string $label 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($label = 'Submit', $options = array()) - { - $options['type'] = 'submit'; - $options['value'] = $label; - return static::tag('input', '', $options); - } - - /** - * Generates a reset input button. - * @param string $label 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($label = 'Reset', $options = array()) - { - $options['type'] = 'reset'; - $options['value'] = $label; - return static::tag('input', '', $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 array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - 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. - * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the radio button will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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, $options = array()) - { - $options['checked'] = (boolean)$checked; - $value = array_key_exists('value', $options) ? $options['value'] : '1'; - 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 = ''; - } - if (isset($options['label'])) { - $label = $options['label']; - $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); - unset($options['label'], $options['labelOptions']); - $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions); - return $hidden . static::tag('div', $content, array('class' => 'radio')); - } else { - 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 array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - 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. - * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the checkbox will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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, $options = array()) - { - $options['checked'] = (boolean)$checked; - $value = array_key_exists('value', $options) ? $options['value'] : '1'; - 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 = ''; - } - if (isset($options['label'])) { - $label = $options['label']; - $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); - unset($options['label'], $options['labelOptions']); - $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions); - return $hidden . static::tag('div', $content, array('class' => 'checkbox')); - } else { - 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 specially handled: - * - * - 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 specially handled: - * - * - 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 (!empty($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. - * @param array $options options (name => config) for the checkbox list container tag. - * The following options are specially handled: - * - * - tag: string, the tag name of the container element. - * - 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. - * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. - * This option is ignored if `item` option is set. - * - 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, respectively. - * @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; - $encode = !isset($options['encode']) || $options['encode']; - $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::checkbox($name, $checked, array( - 'value' => $value, - 'label' => $encode ? static::encode($label) : $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"; - - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); - - return $hidden . static::tag($tag, implode($separator, $lines), $options); - } - - /** - * 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. - * @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. - * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. - * This option is ignored if `item` option is set. - * - 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, respectively. - * @return string the generated radio button list - */ - public static function radioList($name, $selection = null, $items = array(), $options = array()) - { - $encode = !isset($options['encode']) || $options['encode']; - $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::radio($name, $checked, array( - 'value' => $value, - 'label' => $encode ? static::encode($label) : $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 = ''; - } - - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); - - return $hidden . static::tag($tag, implode($separator, $lines), $options); - } - - /** - * Generates an unordered list. - * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. - * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. - * @param array $options options (name => config) for the radio button list. The following options are supported: - * - * - encode: boolean, whether to HTML-encode the items. Defaults to true. - * This option is ignored if the `item` option is specified. - * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. - * - item: callable, a callback that is used to generate each individual list item. - * The signature of this callback must be: - * - * ~~~ - * function ($item, $index) - * ~~~ - * - * where $index is the array key corresponding to `$item` in `$items`. The callback should return - * the whole list item tag. - * - * @return string the generated unordered list. An empty string is returned if `$items` is empty. - */ - public static function ul($items, $options = array()) - { - if (empty($items)) { - return ''; - } - $tag = isset($options['tag']) ? $options['tag'] : 'ul'; - $encode = !isset($options['encode']) || $options['encode']; - $formatter = isset($options['item']) ? $options['item'] : null; - $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : array(); - unset($options['tag'], $options['encode'], $options['item'], $options['itemOptions']); - $results = array(); - foreach ($items as $index => $item) { - if ($formatter !== null) { - $results[] = call_user_func($formatter, $item, $index); - } else { - $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions); - } - } - return static::tag($tag, "\n" . implode("\n", $results) . "\n", $options); - } - - /** - * Generates an ordered list. - * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. - * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. - * @param array $options options (name => config) for the radio button list. The following options are supported: - * - * - encode: boolean, whether to HTML-encode the items. Defaults to true. - * This option is ignored if the `item` option is specified. - * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. - * - item: callable, a callback that is used to generate each individual list item. - * The signature of this callback must be: - * - * ~~~ - * function ($item, $index) - * ~~~ - * - * where $index is the array key corresponding to `$item` in `$items`. The callback should return - * the whole list item tag. - * - * @return string the generated ordered list. An empty string is returned if `$items` is empty. - */ - public static function ol($items, $options = array()) - { - $options['tag'] = 'ol'; - return static::ul($items, $options); - } - - /** - * Generates a label tag for the given model attribute. - * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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. - * The following options are specially handled: - * - * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]]. - * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display - * (after encoding). - * - * @return string the generated label tag - */ - public static function activeLabel($model, $attribute, $options = array()) - { - $attribute = static::getAttributeName($attribute); - $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute)); - $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute); - unset($options['label'], $options['for']); - return static::label($label, $for, $options); - } - - /** - * Generates a tag that contains the first validation error of the specified model attribute. - * Note that even if there is no validation error, this method will still return an empty error tag. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded - * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. - * - * The following options are specially handled: - * - * - tag: this specifies the tag name. If not set, "div" will be used. - * - * @return string the generated label tag - */ - public static function error($model, $attribute, $options = array()) - { - $attribute = static::getAttributeName($attribute); - $error = $model->getFirstError($attribute); - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag']); - return Html::tag($tag, Html::encode($error), $options); - } - - /** - * Generates an input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param string $type the input type (e.g. 'text', 'password') - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeInput($type, $model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::input($type, $name, $value, $options); - } - - /** - * Generates a text input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeTextInput($model, $attribute, $options = array()) - { - return static::activeInput('text', $model, $attribute, $options); - } - - /** - * Generates a hidden input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeHiddenInput($model, $attribute, $options = array()) - { - return static::activeInput('hidden', $model, $attribute, $options); - } - - /** - * Generates a password input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activePasswordInput($model, $attribute, $options = array()) - { - return static::activeInput('password', $model, $attribute, $options); - } - - /** - * Generates a file input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeFileInput($model, $attribute, $options = array()) - { - // add a hidden field so that if a model only has a file field, we can - // still use isset($_POST[$modelClass]) to detect if the input is submitted - return static::activeHiddenInput($model, $attribute, array('id' => null, 'value' => '')) - . static::activeInput('file', $model, $attribute, $options); - } - - /** - * Generates a textarea tag for the given model attribute. - * The model attribute value will be used as the content in the textarea. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated textarea tag - */ - public static function activeTextarea($model, $attribute, $options = array()) - { - $name = static::getInputName($model, $attribute); - $value = static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::textarea($name, $value, $options); - } - - /** - * Generates a radio button tag for the given model attribute. - * This method will generate the "checked" tag attribute according to the model attribute value. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, - * it will take the default value '0'. This method will render a hidden input 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. - * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the radio button will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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 activeRadio($model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('uncheck', $options)) { - $options['uncheck'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::radio($name, $checked, $options); - } - - /** - * Generates a checkbox tag for the given model attribute. - * This method will generate the "checked" tag attribute according to the model attribute value. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, - * it will take the default value '0'. This method will render a hidden input 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. - * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the checkbox will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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 activeCheckbox($model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('uncheck', $options)) { - $options['uncheck'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::checkbox($name, $checked, $options); - } - - /** - * Generates a drop-down list for the given model attribute. - * The selection of the drop-down list is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeDropDownList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::dropDownList($name, $checked, $items, $options); - } - - /** - * Generates a list box. - * The selection of the list box is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeListBox($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::listBox($name, $checked, $items, $options); - } - - /** - * Generates a list of checkboxes. - * A checkbox list allows multiple selection, like [[listBox()]]. - * As a result, the corresponding submitted value is an array. - * The selection of the checkbox list is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeCheckboxList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::checkboxList($name, $checked, $items, $options); - } - - /** - * Generates a list of radio buttons. - * A radio button list is like a checkbox list, except that it only allows single selection. - * The selection of the radio buttons is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeRadioList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::radioList($name, $checked, $items, $options); - } - - /** - * 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'] = (string)$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. - * Attributes whose values are of boolean type will be treated as - * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes). - * And attributes whose values are null will not be rendered. - * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. - * @return string the rendering result. If the attributes are not empty, they will be rendered - * into a string with a leading white space (so 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 (is_bool($value)) { - if ($value) { - $html .= " $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 first be processed by [[Yii::getAlias()]]. If the result - * is an absolute URL, it will be returned without any change further; Otherwise, the result - * will be prefixed with [[\yii\web\Request::baseUrl]] 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')`. - * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used. - * - * @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 instanceof \yii\web\Controller) { - 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 { - $url = Yii::getAlias($url); - if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) { - return $url; - } else { - return Yii::$app->getRequest()->getBaseUrl() . '/' . $url; - } - } - } - - /** - * Adds a CSS class to the specified options. - * If the CSS class is already in the options, it will not be added again. - * @param array $options the options to be modified. - * @param string $class the CSS class to be added - */ - public static function addCssClass(&$options, $class) - { - if (isset($options['class'])) { - $classes = ' ' . $options['class'] . ' '; - if (($pos = strpos($classes, ' ' . $class . ' ')) === false) { - $options['class'] .= ' ' . $class; - } - } else { - $options['class'] = $class; - } - } - - /** - * Removes a CSS class from the specified options. - * @param array $options the options to be modified. - * @param string $class the CSS class to be removed - */ - public static function removeCssClass(&$options, $class) - { - if (isset($options['class'])) { - $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY)); - if (($index = array_search($class, $classes)) !== false) { - unset($classes[$index]); - } - if (empty($classes)) { - unset($options['class']); - } else { - $options['class'] = implode(' ', $classes); - } - } - } - - /** - * Returns the real attribute name from the given attribute expression. - * - * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. - * It is mainly used in tabular data input and/or input of array type. Below are some examples: - * - * - `[0]content` is used in tabular data input to represent the "content" attribute - * for the first model in tabular input; - * - `dates[0]` represents the first array element of the "dates" attribute; - * - `[0]dates[0]` represents the first array element of the "dates" attribute - * for the first model in tabular input. - * - * If `$attribute` has neither prefix nor suffix, it will be returned back without change. - * @param string $attribute the attribute name or expression - * @return string the attribute name without prefix and suffix. - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getAttributeName($attribute) - { - if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - return $matches[2]; - } else { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - } - - /** - * Returns the value of the specified attribute name or expression. - * - * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`. - * See [[getAttributeName()]] for more details about attribute expression. - * - * @param Model $model the model object - * @param string $attribute the attribute name or expression - * @return mixed the corresponding attribute value - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getAttributeValue($model, $attribute) - { - if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - $attribute = $matches[2]; - $index = $matches[3]; - if ($index === '') { - return $model->$attribute; - } else { - $value = $model->$attribute; - foreach (explode('][', trim($index, '[]')) as $id) { - if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) { - $value = $value[$id]; - } else { - return null; - } - } - return $value; - } - } - - /** - * Generates an appropriate input name for the specified attribute name or expression. - * - * This method generates a name that can be used as the input name to collect user input - * for the specified attribute. The name is generated according to the [[Model::formName|form name]] - * of the model and the given attribute name. For example, if the form name of the `Post` model - * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`. - * - * See [[getAttributeName()]] for explanation of attribute expression. - * - * @param Model $model the model object - * @param string $attribute the attribute name or expression - * @return string the generated input name - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getInputName($model, $attribute) - { - $formName = $model->formName(); - if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - $prefix = $matches[1]; - $attribute = $matches[2]; - $suffix = $matches[3]; - if ($formName === '' && $prefix === '') { - return $attribute . $suffix; - } elseif ($formName !== '') { - return $formName . $prefix . "[$attribute]" . $suffix; - } else { - throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.'); - } - } - - /** - * Generates an appropriate input ID for the specified attribute name or expression. - * - * This method converts the result [[getInputName()]] into a valid input ID. - * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression. - * @return string the generated input ID - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getInputId($model, $attribute) - { - $name = strtolower(static::getInputName($model, $attribute)); - return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name); - } -} diff --git a/framework/yii/helpers/HtmlPurifier.php b/framework/yii/helpers/HtmlPurifier.php index f7203e4..ca7e485 100644 --- a/framework/yii/helpers/HtmlPurifier.php +++ b/framework/yii/helpers/HtmlPurifier.php @@ -32,6 +32,6 @@ namespace yii\helpers; * @author Alexander Makarov * @since 2.0 */ -class HtmlPurifier extends HtmlPurifierBase +class HtmlPurifier extends AbstractHtmlPurifier { } diff --git a/framework/yii/helpers/HtmlPurifierBase.php b/framework/yii/helpers/HtmlPurifierBase.php deleted file mode 100644 index e89a589..0000000 --- a/framework/yii/helpers/HtmlPurifierBase.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @since 2.0 - */ -class HtmlPurifierBase -{ - /** - * Passes markup through HTMLPurifier making it safe to output to end user - * - * @param string $content - * @param array|null $config - * @return string - */ - public static function process($content, $config = null) - { - $configInstance = \HTMLPurifier_Config::create($config); - $configInstance->autoFinalize = false; - $purifier=\HTMLPurifier::instance($configInstance); - $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath()); - return $purifier->purify($content); - } -} diff --git a/framework/yii/helpers/Inflector.php b/framework/yii/helpers/Inflector.php index ba9c069..71e7f05 100644 --- a/framework/yii/helpers/Inflector.php +++ b/framework/yii/helpers/Inflector.php @@ -13,6 +13,6 @@ namespace yii\helpers; * @author Antonio Ramirez * @since 2.0 */ -class Inflector extends InflectorBase +class Inflector extends AbstractInflector { } diff --git a/framework/yii/helpers/InflectorBase.php b/framework/yii/helpers/InflectorBase.php deleted file mode 100644 index 87c1ff4..0000000 --- a/framework/yii/helpers/InflectorBase.php +++ /dev/null @@ -1,480 +0,0 @@ - - * @since 2.0 - */ -class InflectorBase -{ - /** - * @var array the rules for converting a word into its plural form. - * The keys are the regular expressions and the values are the corresponding replacements. - */ - public static $plurals = array( - '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1', - '/^(sea[- ]bass)$/i' => '\1', - '/(m)ove$/i' => '\1oves', - '/(f)oot$/i' => '\1eet', - '/(h)uman$/i' => '\1umans', - '/(s)tatus$/i' => '\1tatuses', - '/(s)taff$/i' => '\1taff', - '/(t)ooth$/i' => '\1eeth', - '/(quiz)$/i' => '\1zes', - '/^(ox)$/i' => '\1\2en', - '/([m|l])ouse$/i' => '\1ice', - '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', - '/(x|ch|ss|sh)$/i' => '\1es', - '/([^aeiouy]|qu)y$/i' => '\1ies', - '/(hive)$/i' => '\1s', - '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', - '/sis$/i' => 'ses', - '/([ti])um$/i' => '\1a', - '/(p)erson$/i' => '\1eople', - '/(m)an$/i' => '\1en', - '/(c)hild$/i' => '\1hildren', - '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes', - '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', - '/us$/i' => 'uses', - '/(alias)$/i' => '\1es', - '/(ax|cris|test)is$/i' => '\1es', - '/s$/' => 's', - '/^$/' => '', - '/$/' => 's', - ); - /** - * @var array the rules for converting a word into its singular form. - * The keys are the regular expressions and the values are the corresponding replacements. - */ - public static $singulars = array( - '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1', - '/^(sea[- ]bass)$/i' => '\1', - '/(s)tatuses$/i' => '\1tatus', - '/(f)eet$/i' => '\1oot', - '/(t)eeth$/i' => '\1ooth', - '/^(.*)(menu)s$/i' => '\1\2', - '/(quiz)zes$/i' => '\\1', - '/(matr)ices$/i' => '\1ix', - '/(vert|ind)ices$/i' => '\1ex', - '/^(ox)en/i' => '\1', - '/(alias)(es)*$/i' => '\1', - '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', - '/([ftw]ax)es/i' => '\1', - '/(cris|ax|test)es$/i' => '\1is', - '/(shoe|slave)s$/i' => '\1', - '/(o)es$/i' => '\1', - '/ouses$/' => 'ouse', - '/([^a])uses$/' => '\1us', - '/([m|l])ice$/i' => '\1ouse', - '/(x|ch|ss|sh)es$/i' => '\1', - '/(m)ovies$/i' => '\1\2ovie', - '/(s)eries$/i' => '\1\2eries', - '/([^aeiouy]|qu)ies$/i' => '\1y', - '/([lr])ves$/i' => '\1f', - '/(tive)s$/i' => '\1', - '/(hive)s$/i' => '\1', - '/(drive)s$/i' => '\1', - '/([^fo])ves$/i' => '\1fe', - '/(^analy)ses$/i' => '\1sis', - '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', - '/([ti])a$/i' => '\1um', - '/(p)eople$/i' => '\1\2erson', - '/(m)en$/i' => '\1an', - '/(c)hildren$/i' => '\1\2hild', - '/(n)ews$/i' => '\1\2ews', - '/eaus$/' => 'eau', - '/^(.*us)$/' => '\\1', - '/s$/i' => '', - ); - /** - * @var array the special rules for converting a word between its plural form and singular form. - * The keys are the special words in singular form, and the values are the corresponding plural form. - */ - public static $specials = array( - 'atlas' => 'atlases', - 'beef' => 'beefs', - 'brother' => 'brothers', - 'cafe' => 'cafes', - 'child' => 'children', - 'cookie' => 'cookies', - 'corpus' => 'corpuses', - 'cow' => 'cows', - 'curve' => 'curves', - 'foe' => 'foes', - 'ganglion' => 'ganglions', - 'genie' => 'genies', - 'genus' => 'genera', - 'graffito' => 'graffiti', - 'hoof' => 'hoofs', - 'loaf' => 'loaves', - 'man' => 'men', - 'money' => 'monies', - 'mongoose' => 'mongooses', - 'move' => 'moves', - 'mythos' => 'mythoi', - 'niche' => 'niches', - 'numen' => 'numina', - 'occiput' => 'occiputs', - 'octopus' => 'octopuses', - 'opus' => 'opuses', - 'ox' => 'oxen', - 'penis' => 'penises', - 'sex' => 'sexes', - 'soliloquy' => 'soliloquies', - 'testis' => 'testes', - 'trilby' => 'trilbys', - 'turf' => 'turfs', - 'wave' => 'waves', - 'Amoyese' => 'Amoyese', - 'bison' => 'bison', - 'Borghese' => 'Borghese', - 'bream' => 'bream', - 'breeches' => 'breeches', - 'britches' => 'britches', - 'buffalo' => 'buffalo', - 'cantus' => 'cantus', - 'carp' => 'carp', - 'chassis' => 'chassis', - 'clippers' => 'clippers', - 'cod' => 'cod', - 'coitus' => 'coitus', - 'Congoese' => 'Congoese', - 'contretemps' => 'contretemps', - 'corps' => 'corps', - 'debris' => 'debris', - 'diabetes' => 'diabetes', - 'djinn' => 'djinn', - 'eland' => 'eland', - 'elk' => 'elk', - 'equipment' => 'equipment', - 'Faroese' => 'Faroese', - 'flounder' => 'flounder', - 'Foochowese' => 'Foochowese', - 'gallows' => 'gallows', - 'Genevese' => 'Genevese', - 'Genoese' => 'Genoese', - 'Gilbertese' => 'Gilbertese', - 'graffiti' => 'graffiti', - 'headquarters' => 'headquarters', - 'herpes' => 'herpes', - 'hijinks' => 'hijinks', - 'Hottentotese' => 'Hottentotese', - 'information' => 'information', - 'innings' => 'innings', - 'jackanapes' => 'jackanapes', - 'Kiplingese' => 'Kiplingese', - 'Kongoese' => 'Kongoese', - 'Lucchese' => 'Lucchese', - 'mackerel' => 'mackerel', - 'Maltese' => 'Maltese', - 'mews' => 'mews', - 'moose' => 'moose', - 'mumps' => 'mumps', - 'Nankingese' => 'Nankingese', - 'news' => 'news', - 'nexus' => 'nexus', - 'Niasese' => 'Niasese', - 'Pekingese' => 'Pekingese', - 'Piedmontese' => 'Piedmontese', - 'pincers' => 'pincers', - 'Pistoiese' => 'Pistoiese', - 'pliers' => 'pliers', - 'Portuguese' => 'Portuguese', - 'proceedings' => 'proceedings', - 'rabies' => 'rabies', - 'rice' => 'rice', - 'rhinoceros' => 'rhinoceros', - 'salmon' => 'salmon', - 'Sarawakese' => 'Sarawakese', - 'scissors' => 'scissors', - 'series' => 'series', - 'Shavese' => 'Shavese', - 'shears' => 'shears', - 'siemens' => 'siemens', - 'species' => 'species', - 'swine' => 'swine', - 'testes' => 'testes', - 'trousers' => 'trousers', - 'trout' => 'trout', - 'tuna' => 'tuna', - 'Vermontese' => 'Vermontese', - 'Wenchowese' => 'Wenchowese', - 'whiting' => 'whiting', - 'wildebeest' => 'wildebeest', - 'Yengeese' => 'Yengeese', - ); - /** - * @var array map of special chars and its translation. This is used by [[slug()]]. - */ - public static $transliteration = array( - '/ä|æ|ǽ/' => 'ae', - '/ö|œ/' => 'oe', - '/ü/' => 'ue', - '/Ä/' => 'Ae', - '/Ü/' => 'Ue', - '/Ö/' => 'Oe', - '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', - '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', - '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', - '/ç|ć|ĉ|ċ|č/' => 'c', - '/Ð|Ď|Đ/' => 'D', - '/ð|ď|đ/' => 'd', - '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E', - '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e', - '/Ĝ|Ğ|Ġ|Ģ/' => 'G', - '/ĝ|ğ|ġ|ģ/' => 'g', - '/Ĥ|Ħ/' => 'H', - '/ĥ|ħ/' => 'h', - '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I', - '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i', - '/Ĵ/' => 'J', - '/ĵ/' => 'j', - '/Ķ/' => 'K', - '/ķ/' => 'k', - '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L', - '/ĺ|ļ|ľ|ŀ|ł/' => 'l', - '/Ñ|Ń|Ņ|Ň/' => 'N', - '/ñ|ń|ņ|ň|ʼn/' => 'n', - '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O', - '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o', - '/Ŕ|Ŗ|Ř/' => 'R', - '/ŕ|ŗ|ř/' => 'r', - '/Ś|Ŝ|Ş|Ș|Š/' => 'S', - '/ś|ŝ|ş|ș|š|ſ/' => 's', - '/Ţ|Ț|Ť|Ŧ/' => 'T', - '/ţ|ț|ť|ŧ/' => 't', - '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U', - '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u', - '/Ý|Ÿ|Ŷ/' => 'Y', - '/ý|ÿ|ŷ/' => 'y', - '/Ŵ/' => 'W', - '/ŵ/' => 'w', - '/Ź|Ż|Ž/' => 'Z', - '/ź|ż|ž/' => 'z', - '/Æ|Ǽ/' => 'AE', - '/ß/' => 'ss', - '/IJ/' => 'IJ', - '/ij/' => 'ij', - '/Œ/' => 'OE', - '/ƒ/' => 'f' - ); - - /** - * 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 $word the word to be pluralized - * @return string the pluralized word - */ - public static function pluralize($word) - { - if (isset(self::$specials[$word])) { - return self::$specials[$word]; - } - foreach (static::$plurals as $rule => $replacement) { - if (preg_match($rule, $word)) { - return preg_replace($rule, $replacement, $word); - } - } - return $word; - } - - /** - * Returns the singular of the $word - * @param string $word the english word to singularize - * @return string Singular noun. - */ - public static function singularize($word) - { - $result = array_search($word, self::$specials, true); - if ($result !== false) { - return $result; - } - foreach (static::$singulars as $rule => $replacement) { - if (preg_match($rule, $word)) { - return preg_replace($rule, $replacement, $word); - } - } - return $word; - } - - /** - * Converts an underscored or CamelCase word into a English - * sentence. - * @param string $words - * @param bool $ucAll whether to set all words to uppercase - * @return string - */ - public static function titleize($words, $ucAll = false) - { - $words = static::humanize(static::underscore($words), $ucAll); - return $ucAll ? ucwords($words) : ucfirst($words); - } - - /** - * Returns given word as CamelCased - * Converts a word like "send_email" to "SendEmail". It - * will remove non alphanumeric character from the word, so - * "who's online" will be converted to "WhoSOnline" - * @see variablize - * @param string $word the word to CamelCase - * @return string - */ - public static function camelize($word) - { - return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); - } - - /** - * 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('/(? ' ', - '/\\s+/' => $replacement, - '/(?<=[a-z])([A-Z])/' => $replacement . '\\1', - str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => '' - ); - return preg_replace(array_keys($map), array_values($map), $string); - } - - /** - * Converts a table name to its class name. For example, converts "people" to "Person" - * @param string $tableName - * @return string - */ - public static function classify($tableName) - { - return static::camelize(static::singularize($tableName)); - } - - /** - * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ... - * @param int $number the number to get its ordinal value - * @return string - */ - public static function ordinalize($number) - { - if (in_array(($number % 100), range(11, 13))) { - return $number . 'th'; - } - switch ($number % 10) { - case 1: return $number . 'st'; - case 2: return $number . 'nd'; - case 3: return $number . 'rd'; - default: return $number . 'th'; - } - } -} diff --git a/framework/yii/helpers/Json.php b/framework/yii/helpers/Json.php index 424de1f..8544a61 100644 --- a/framework/yii/helpers/Json.php +++ b/framework/yii/helpers/Json.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class Json extends JsonBase +class Json extends AbstractJson { } diff --git a/framework/yii/helpers/JsonBase.php b/framework/yii/helpers/JsonBase.php deleted file mode 100644 index fa3fb01..0000000 --- a/framework/yii/helpers/JsonBase.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @since 2.0 - */ -class JsonBase -{ - /** - * Encodes the given value into a JSON string. - * The method enhances `json_encode()` by supporting JavaScript expressions. - * In particular, the method will not encode a JavaScript expression that is - * represented in terms of a [[JsExpression]] object. - * @param mixed $value the data to be encoded - * @param integer $options the encoding options. For more details please refer to - * [[http://www.php.net/manual/en/function.json-encode.php]] - * @return string the encoding result - */ - public static function encode($value, $options = 0) - { - $expressions = array(); - $value = static::processData($value, $expressions, uniqid()); - $json = json_encode($value, $options); - return empty($expressions) ? $json : strtr($json, $expressions); - } - - /** - * Decodes the given JSON string into a PHP data structure. - * @param string $json the JSON string to be decoded - * @param boolean $asArray whether to return objects in terms of associative arrays. - * @return mixed the PHP data - * @throws InvalidParamException if there is any decoding error - */ - public static function decode($json, $asArray = true) - { - if (is_array($json)) { - throw new InvalidParamException('Invalid JSON data.'); - } - $decode = json_decode((string)$json, $asArray); - switch (json_last_error()) { - case JSON_ERROR_NONE: - break; - case JSON_ERROR_DEPTH: - throw new InvalidParamException('The maximum stack depth has been exceeded.'); - case JSON_ERROR_CTRL_CHAR: - throw new InvalidParamException('Control character error, possibly incorrectly encoded.'); - case JSON_ERROR_SYNTAX: - throw new InvalidParamException('Syntax error.'); - case JSON_ERROR_STATE_MISMATCH: - throw new InvalidParamException('Invalid or malformed JSON.'); - case JSON_ERROR_UTF8: - throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.'); - default: - throw new InvalidParamException('Unknown JSON decoding error.'); - } - - return $decode; - } - - /** - * Pre-processes the data before sending it to `json_encode()`. - * @param mixed $data the data to be processed - * @param array $expressions collection of JavaScript expressions - * @param string $expPrefix a prefix internally used to handle JS expressions - * @return mixed the processed data - */ - protected static function processData($data, &$expressions, $expPrefix) - { - if (is_array($data)) { - foreach ($data as $key => $value) { - if (is_array($value) || is_object($value)) { - $data[$key] = static::processData($value, $expressions, $expPrefix); - } - } - return $data; - } elseif (is_object($data)) { - if ($data instanceof JsExpression) { - $token = "!{[$expPrefix=" . count($expressions) . ']}!'; - $expressions['"' . $token . '"'] = $data->expression; - return $token; - } else { - $data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data); - $result = array(); - foreach ($data as $key => $value) { - if (is_array($value) || is_object($value)) { - $result[$key] = static::processData($value, $expressions, $expPrefix); - } else { - $result[$key] = $value; - } - } - return $result; - } - } else { - return $data; - } - } -} diff --git a/framework/yii/helpers/Markdown.php b/framework/yii/helpers/Markdown.php index 690df5d..89f8801 100644 --- a/framework/yii/helpers/Markdown.php +++ b/framework/yii/helpers/Markdown.php @@ -30,6 +30,6 @@ namespace yii\helpers; * @author Alexander Makarov * @since 2.0 */ -class Markdown extends MarkdownBase +class Markdown extends AbstractMarkdown { } diff --git a/framework/yii/helpers/MarkdownBase.php b/framework/yii/helpers/MarkdownBase.php deleted file mode 100644 index 9db5b1e..0000000 --- a/framework/yii/helpers/MarkdownBase.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @since 2.0 - */ -class MarkdownBase -{ - /** - * @var MarkdownExtra - */ - protected static $markdown; - - /** - * Converts markdown into HTML - * - * @param string $content - * @param array $config - * @return string - */ - public static function process($content, $config = array()) - { - if (static::$markdown === null) { - static::$markdown = new MarkdownExtra(); - } - foreach ($config as $name => $value) { - static::$markdown->{$name} = $value; - } - return static::$markdown->transform($content); - } -} diff --git a/framework/yii/helpers/Security.php b/framework/yii/helpers/Security.php index d0ca2ed..e2c0314 100644 --- a/framework/yii/helpers/Security.php +++ b/framework/yii/helpers/Security.php @@ -24,6 +24,6 @@ namespace yii\helpers; * @author Tom Worster * @since 2.0 */ -class Security extends SecurityBase +class Security extends AbstractSecurity { } diff --git a/framework/yii/helpers/SecurityBase.php b/framework/yii/helpers/SecurityBase.php deleted file mode 100644 index 5b192de..0000000 --- a/framework/yii/helpers/SecurityBase.php +++ /dev/null @@ -1,285 +0,0 @@ - - * @author Tom Worster - * @since 2.0 - */ -class SecurityBase -{ - /** - * 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(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); - 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(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); - $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 = array(); - if (is_file($keyFile)) { - $keys = require($keyFile); - } - } - if (!isset($keys[$name])) { - $keys[$name] = static::generateRandomKey($length); - file_put_contents($keyFile, " 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 > 31) { - 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; - } -} diff --git a/framework/yii/helpers/StringHelper.php b/framework/yii/helpers/StringHelper.php index ef75790..e367c59 100644 --- a/framework/yii/helpers/StringHelper.php +++ b/framework/yii/helpers/StringHelper.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Alex Makarov * @since 2.0 */ -class StringHelper extends StringHelperBase +class StringHelper extends AbstractStringHelper { } diff --git a/framework/yii/helpers/StringHelperBase.php b/framework/yii/helpers/StringHelperBase.php deleted file mode 100644 index cbb696e..0000000 --- a/framework/yii/helpers/StringHelperBase.php +++ /dev/null @@ -1,138 +0,0 @@ - - * @author Alex Makarov - * @since 2.0 - */ -class StringHelperBase -{ - /** - * Returns the number of bytes in the given string. - * This method ensures the string is treated as a byte array by using `mb_strlen()`. - * @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 mb_strlen($string, '8bit'); - } - - /** - * Returns the portion of string specified by the start and length parameters. - * This method ensures the string is treated as a byte array by using `mb_substr()`. - * @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 mb_substr($string, $start, $length, '8bit'); - } - - /** - * Returns the trailing name component of a path. - * This method is similar to the php function `basename()` except that it will - * treat both \ and / as directory separators, independent of the operating system. - * This method was mainly created to work on php namespaces. When working with real - * file paths, php's `basename()` should work fine for you. - * Note: this method is not aware of the actual filesystem, or path components such as "..". - * @param string $path A path string. - * @param string $suffix If the name component ends in suffix this will also be cut off. - * @return string the trailing name component of the given path. - * @see http://www.php.net/manual/en/function.basename.php - */ - public static function basename($path, $suffix = '') - { - if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) { - $path = mb_substr($path, 0, -$len); - } - $path = rtrim(str_replace('\\', '/', $path), '/\\'); - if (($pos = mb_strrpos($path, '/')) !== false) { - return mb_substr($path, $pos + 1); - } - return $path; - } - - /** - * Returns parent directory's path. - * This method is similar to `dirname()` except that it will treat - * both \ and / as directory separators, independent of the operating system. - * @param string $path A path string. - * @return string the parent directory's path. - * @see http://www.php.net/manual/en/function.basename.php - */ - public static function dirname($path) - { - $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); - if ($pos !== false) { - return mb_substr($path, 0, $pos); - } else { - return $path; - } - } - - /** - * Compares two strings or string arrays, and return their differences. - * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package. - * @param string|array $lines1 the first string or string array to be compared. If it is a string, - * it will be converted into a string array by breaking at newlines. - * @param string|array $lines2 the second string or string array to be compared. If it is a string, - * it will be converted into a string array by breaking at newlines. - * @param string $format the output format. It must be 'inline', 'unified', 'context', 'side-by-side', or 'array'. - * @return string|array the comparison result. An array is returned if `$format` is 'array'. For all other - * formats, a string is returned. - * @throws InvalidParamException if the format is invalid. - */ - public static function diff($lines1, $lines2, $format = 'inline') - { - if (!is_array($lines1)) { - $lines1 = explode("\n", $lines1); - } - if (!is_array($lines2)) { - $lines2 = explode("\n", $lines2); - } - foreach ($lines1 as $i => $line) { - $lines1[$i] = rtrim($line, "\r\n"); - } - foreach ($lines2 as $i => $line) { - $lines2[$i] = rtrim($line, "\r\n"); - } - switch ($format) { - case 'inline': - $renderer = new \Diff_Renderer_Html_Inline(); - break; - case 'array': - $renderer = new \Diff_Renderer_Html_Array(); - break; - case 'side-by-side': - $renderer = new \Diff_Renderer_Html_SideBySide(); - break; - case 'context': - $renderer = new \Diff_Renderer_Text_Context(); - break; - case 'unified': - $renderer = new \Diff_Renderer_Text_Unified(); - break; - default: - throw new InvalidParamException("Output format must be 'inline', 'side-by-side', 'array', 'context' or 'unified'."); - } - $diff = new \Diff($lines1, $lines2); - return $diff->render($renderer); - } -} diff --git a/framework/yii/helpers/VarDumper.php b/framework/yii/helpers/VarDumper.php index 50e543c..0aae16e 100644 --- a/framework/yii/helpers/VarDumper.php +++ b/framework/yii/helpers/VarDumper.php @@ -23,6 +23,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class VarDumper extends VarDumperBase +class VarDumper extends AbstractVarDumper { } diff --git a/framework/yii/helpers/VarDumperBase.php b/framework/yii/helpers/VarDumperBase.php deleted file mode 100644 index c7da208..0000000 --- a/framework/yii/helpers/VarDumperBase.php +++ /dev/null @@ -1,127 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2011 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\helpers; - -/** - * VarDumperBase provides concrete implementation for [[VarDumper]]. - * - * Do not use VarDumperBase. Use [[VarDumper]] instead. - * - * @author Qiang Xue - * @since 2.0 - */ -class VarDumperBase -{ - 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 static::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("/', '', $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 = array_push(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; - } - } -} From b59812507acfac516b12dbc8364b67506406438f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 27 Sep 2013 18:39:33 -0400 Subject: [PATCH 2/4] reverted doc change: the original one is correct. --- framework/yii/web/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index b353e7c..e6505fd 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -581,7 +581,7 @@ class Response extends \yii\base\Response * In a controller action you may use this method like this: * * ~~~ - * return Yii::$app->getResponse()->redirect($url)->send(); + * return Yii::$app->getResponse()->redirect($url); * ~~~ * * @param string|array $url the URL to be redirected to. This can be in one of the following formats: From 558f499439fe9adfd9416dfde8944181ae35c3aa Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 28 Sep 2013 02:48:54 +0400 Subject: [PATCH 3/4] Renamed YiiBase to AbstractYii --- build/build.xml | 2 +- build/controllers/ClassmapController.php | 2 +- build/controllers/PhpDocController.php | 2 +- docs/internals/autoloader.md | 2 +- framework/yii/AbstractYii.php | 574 +++++++++++++++++++++++++++++++ framework/yii/Yii.php | 6 +- framework/yii/YiiBase.php | 574 ------------------------------- framework/yii/log/Logger.php | 2 +- 8 files changed, 582 insertions(+), 582 deletions(-) create mode 100644 framework/yii/AbstractYii.php delete mode 100644 framework/yii/YiiBase.php diff --git a/build/build.xml b/build/build.xml index 9c18af0..85d5aa5 100644 --- a/build/build.xml +++ b/build/build.xml @@ -265,7 +265,7 @@ Please update yiisite/common/data/versions.php file with the following code: where <target name> can be one of the following: - - sync : synchronize yiilite.php and YiiBase.php + - sync : synchronize yiilite.php and AbstractYii.php - message : extract i18n messages of the framework - src : build source release - doc : build documentation release (Windows only) diff --git a/build/controllers/ClassmapController.php b/build/controllers/ClassmapController.php index 2a0483c..10eb134 100644 --- a/build/controllers/ClassmapController.php +++ b/build/controllers/ClassmapController.php @@ -45,7 +45,7 @@ class ClassmapController extends Controller 'only' => array('.php'), 'except' => array( 'Yii.php', - 'YiiBase.php', + 'AbstractYii.php', '/debug/', '/console/', '/test/', diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 5aac7e5..4fed3b2 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -56,7 +56,7 @@ class PhpDocController extends Controller }, 'only' => array('.php'), 'except' => array( - 'YiiBase.php', + 'AbstractYii.php', 'Yii.php', '/debug/views/', '/requirements/', diff --git a/docs/internals/autoloader.md b/docs/internals/autoloader.md index b7696d7..067773d 100644 --- a/docs/internals/autoloader.md +++ b/docs/internals/autoloader.md @@ -16,4 +16,4 @@ PEAR-style libraries References ---------- -- YiiBase::autoload \ No newline at end of file +- AbstractYii::autoload \ No newline at end of file diff --git a/framework/yii/AbstractYii.php b/framework/yii/AbstractYii.php new file mode 100644 index 0000000..c40aad9 --- /dev/null +++ b/framework/yii/AbstractYii.php @@ -0,0 +1,574 @@ + + * @since 2.0 + */ +abstract class AbstractYii +{ + /** + * @var array class map used by the Yii autoloading mechanism. + * The array keys are the class names (without leading backslashes), and the array values + * are the corresponding class file paths (or path aliases). This property mainly affects + * how [[autoload()]] works. + * @see import + * @see autoload + */ + public static $classMap = array(); + /** + * @var \yii\console\Application|\yii\web\Application the application instance + */ + public static $app; + /** + * @var array registered path aliases + * @see getAlias + * @see setAlias + */ + public static $aliases = array('@yii' => __DIR__); + /** + * @var array initial property values that will be applied to objects newly created via [[createObject]]. + * The array keys are class names without leading backslashes "\", and the array values are the corresponding + * name-value pairs for initializing the created class instances. For example, + * + * ~~~ + * array( + * 'Bar' => array( + * 'prop1' => 'value1', + * 'prop2' => 'value2', + * ), + * 'mycompany\foo\Car' => array( + * 'prop1' => 'value1', + * 'prop2' => 'value2', + * ), + * ) + * ~~~ + * + * @see createObject + */ + public static $objectConfig = array(); + + + /** + * @return string the version of Yii framework + */ + public static function getVersion() + { + return '2.0-dev'; + } + + /** + * Imports a set of namespaces. + * + * By importing a namespace, the method will create an alias for the directory corresponding + * to the namespace. For example, if "foo\bar" is a namespace associated with the directory + * "path/to/foo/bar", then an alias "@foo/bar" will be created for this directory. + * + * This method is typically invoked in the bootstrap file to import the namespaces of + * the installed extensions. By default, Composer, when installing new extensions, will + * generate such a mapping file which can be loaded and passed to this method. + * + * @param array $namespaces the namespaces to be imported. The keys are the namespaces, + * and the values are the corresponding directories. + */ + public static function importNamespaces($namespaces) + { + foreach ($namespaces as $name => $path) { + if ($name !== '') { + $name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/'); + if (is_array($path)) { + $path = reset($path); + } + static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name); + } + } + } + + /** + * Translates a path alias into an actual path. + * + * The translation is done according to the following procedure: + * + * 1. If the given alias does not start with '@', it is returned back without change; + * 2. Otherwise, look for the longest registered alias that matches the beginning part + * of the given alias. If it exists, replace the matching part of the given alias with + * the corresponding registered path. + * 3. Throw an exception or return false, depending on the `$throwException` parameter. + * + * For example, by default '@yii' is registered as the alias to the Yii framework directory, + * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'. + * + * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config' + * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path. + * This is because the longest alias takes precedence. + * + * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced + * instead of '@foo/bar', because '/' serves as the boundary character. + * + * Note, this method does not check if the returned path exists or not. + * + * @param string $alias the alias to be translated. + * @param boolean $throwException whether to throw an exception if the given alias is invalid. + * If this is false and an invalid alias is given, false will be returned by this method. + * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered. + * @throws InvalidParamException if the alias is invalid while $throwException is true. + * @see setAlias + */ + public static function getAlias($alias, $throwException = true) + { + if (strncmp($alias, '@', 1)) { + // not an alias + return $alias; + } + + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + + if (isset(self::$aliases[$root])) { + if (is_string(self::$aliases[$root])) { + return $pos === false ? self::$aliases[$root] : self::$aliases[$root] . substr($alias, $pos); + } else { + foreach (self::$aliases[$root] as $name => $path) { + if (strpos($alias . '/', $name . '/') === 0) { + return $path . substr($alias, strlen($name)); + } + } + } + } + + if ($throwException) { + throw new InvalidParamException("Invalid path alias: $alias"); + } else { + return false; + } + } + + /** + * Returns the root alias part of a given alias. + * A root alias is an alias that has been registered via [[setAlias()]] previously. + * If a given alias matches multiple root aliases, the longest one will be returned. + * @param string $alias the alias + * @return string|boolean the root alias, or false if no root alias is found + */ + public static function getRootAlias($alias) + { + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + + if (isset(self::$aliases[$root])) { + if (is_string(self::$aliases[$root])) { + return $root; + } else { + foreach (self::$aliases[$root] as $name => $path) { + if (strpos($alias . '/', $name . '/') === 0) { + return $name; + } + } + } + } + return false; + } + + /** + * Registers a path alias. + * + * A path alias is a short name representing a long path (a file path, a URL, etc.) + * For example, we use '@yii' as the alias of the path to the Yii framework directory. + * + * A path alias must start with the character '@' so that it can be easily differentiated + * from non-alias paths. + * + * Note that this method does not check if the given path exists or not. All it does is + * to associate the alias with the path. + * + * Any trailing '/' and '\' characters in the given path will be trimmed. + * + * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character. + * It may contain the forward slash '/' which serves as boundary character when performing + * alias translation by [[getAlias()]]. + * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters + * will be trimmed. This can be + * + * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`) + * - a URL (e.g. `http://www.yiiframework.com`) + * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the + * actual path first by calling [[getAlias()]]. + * + * @throws InvalidParamException if $path is an invalid alias. + * @see getAlias + */ + public static function setAlias($alias, $path) + { + if (strncmp($alias, '@', 1)) { + $alias = '@' . $alias; + } + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + if ($path !== null) { + $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); + if (!isset(self::$aliases[$root])) { + if ($pos === false) { + self::$aliases[$root] = $path; + } else { + self::$aliases[$root] = array($alias => $path); + } + } elseif (is_string(self::$aliases[$root])) { + if ($pos === false) { + self::$aliases[$root] = $path; + } else { + self::$aliases[$root] = array( + $alias => $path, + $root => self::$aliases[$root], + ); + } + } else { + self::$aliases[$root][$alias] = $path; + krsort(self::$aliases[$root]); + } + } elseif (isset(self::$aliases[$root])) { + if (is_array(self::$aliases[$root])) { + unset(self::$aliases[$root][$alias]); + } elseif ($pos === false) { + unset(self::$aliases[$root]); + } + } + } + + /** + * Class autoload loader. + * This method is invoked automatically when PHP sees an unknown class. + * The method will attempt to include the class file according to the following procedure: + * + * 1. Search in [[classMap]]; + * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt + * to include the file associated with the corresponding path alias + * (e.g. `@yii/base/Component.php`); + * 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`), + * it will attempt to include the file associated with the corresponding path alias + * (e.g. `@PHPUnit/Framework/TestCase.php`); + * + * This autoloader allows loading classes that follow the [PSR-0 standard](http://www.php-fig.org/psr/0/). + * Therefor a path alias has to be defined for each top-level namespace. + * + * @param string $className the fully qualified class name without a leading backslash "\" + * @throws UnknownClassException if the class does not exist in the class file + */ + public static function autoload($className) + { + if (isset(self::$classMap[$className])) { + $classFile = self::$classMap[$className]; + if ($classFile[0] === '@') { + $classFile = static::getAlias($classFile); + } + } else { + // follow PSR-0 to determine the class file + if (($pos = strrpos($className, '\\')) !== false) { + // namespaced class, e.g. yii\base\Component + $path = str_replace('\\', '/', substr($className, 0, $pos + 1)) + . str_replace('_', '/', substr($className, $pos + 1)) . '.php'; + } else { + $path = str_replace('_', '/', $className) . '.php'; + } + + // try loading via path alias + if (strpos($path, '/') === false) { + return; + } else { + $classFile = static::getAlias('@' . $path, false); + if ($classFile === false || !is_file($classFile)) { + return; + } + } + } + + include($classFile); + + if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && + (!function_exists('trait_exists') || !trait_exists($className, false))) { + throw new UnknownClassException("Unable to find '$className' in file: $classFile"); + } + } + + /** + * Creates a new object using the given configuration. + * + * The configuration can be either a string or an array. + * If a string, it is treated as the *object class*; if an array, + * it must contain a `class` element specifying the *object class*, and + * the rest of the name-value pairs in the array will be used to initialize + * the corresponding object properties. + * + * The object type can be either a class name or the [[getAlias()|alias]] of + * the class. For example, + * + * - `app\components\GoogleMap`: fully-qualified namespaced class. + * - `@app/components/GoogleMap`: an alias, used for non-namespaced class. + * + * Below are some usage examples: + * + * ~~~ + * $object = \Yii::createObject('@app/components/GoogleMap'); + * $object = \Yii::createObject(array( + * 'class' => '\app\components\GoogleMap', + * 'apiKey' => 'xyz', + * )); + * ~~~ + * + * This method can be used to create any object as long as the object's constructor is + * defined like the following: + * + * ~~~ + * public function __construct(..., $config = array()) { + * } + * ~~~ + * + * The method will pass the given configuration as the last parameter of the constructor, + * and any additional parameters to this method will be passed as the rest of the constructor parameters. + * + * @param string|array $config the configuration. It can be either a string representing the class name + * or an array representing the object configuration. + * @return mixed the created object + * @throws InvalidConfigException if the configuration is invalid. + */ + public static function createObject($config) + { + static $reflections = array(); + + if (is_string($config)) { + $class = $config; + $config = array(); + } elseif (isset($config['class'])) { + $class = $config['class']; + unset($config['class']); + } else { + throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); + } + + $class = ltrim($class, '\\'); + + if (isset(self::$objectConfig[$class])) { + $config = array_merge(self::$objectConfig[$class], $config); + } + + if (($n = func_num_args()) > 1) { + /** @var $reflection \ReflectionClass */ + if (isset($reflections[$class])) { + $reflection = $reflections[$class]; + } else { + $reflection = $reflections[$class] = new \ReflectionClass($class); + } + $args = func_get_args(); + array_shift($args); // remove $config + if (!empty($config)) { + $args[] = $config; + } + return $reflection->newInstanceArgs($args); + } else { + return empty($config) ? new $class : new $class($config); + } + } + + /** + * Logs a trace message. + * Trace messages are logged mainly for development purpose to see + * the execution work flow of some code. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function trace($message, $category = 'application') + { + if (YII_DEBUG) { + self::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category); + } + } + + /** + * Logs an error message. + * An error message is typically logged when an unrecoverable error occurs + * during the execution of an application. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function error($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category); + } + + /** + * Logs a warning message. + * A warning message is typically logged when an error occurs while the execution + * can still continue. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function warning($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category); + } + + /** + * Logs an informative message. + * An informative message is typically logged by an application to keep record of + * something important (e.g. an administrator logs in). + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function info($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_INFO, $category); + } + + /** + * Marks the beginning of a code block for profiling. + * This has to be matched with a call to [[endProfile]] with the same category name. + * The begin- and end- calls must also be properly nested. For example, + * + * ~~~ + * \Yii::beginProfile('block1'); + * // some code to be profiled + * \Yii::beginProfile('block2'); + * // some other code to be profiled + * \Yii::endProfile('block2'); + * \Yii::endProfile('block1'); + * ~~~ + * @param string $token token for the code block + * @param string $category the category of this log message + * @see endProfile + */ + public static function beginProfile($token, $category = 'application') + { + self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); + } + + /** + * Marks the end of a code block for profiling. + * This has to be matched with a previous call to [[beginProfile]] with the same category name. + * @param string $token token for the code block + * @param string $category the category of this log message + * @see beginProfile + */ + public static function endProfile($token, $category = 'application') + { + self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category); + } + + /** + * Returns an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information. + * @return string an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information + */ + public static function powered() + { + return 'Powered by Yii Framework'; + } + + /** + * Translates a message to the specified language. + * + * This is a shortcut method of [[\yii\i18n\I18N::translate()]]. + * + * The translation will be conducted according to the message category and the target language will be used. + * + * In case when a translated message has different plural forms (separated by "|"), this method + * will also attempt to choose an appropriate one according to a given numeric value which is + * specified as the first parameter (indexed by 0) in `$params`. + * + * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first + * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}" + * will be replaced with the given number. + * + * For more details on how plural rules are applied, please refer to: + * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]] + * + * @param string $category the message category. + * @param string $message the message to be translated. + * @param array $params the parameters that will be used to replace the corresponding placeholders in the message. + * @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current + * [[\yii\base\Application::language|application language]] will be used. + * @return string the translated message. + */ + public static function t($category, $message, $params = array(), $language = null) + { + if (self::$app !== null) { + return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language); + } else { + return is_array($params) ? strtr($message, $params) : $message; + } + } + + /** + * Configures an object with the initial property values. + * @param object $object the object to be configured + * @param array $properties the property initial values given in terms of name-value pairs. + */ + public static function configure($object, $properties) + { + foreach ($properties as $name => $value) { + $object->$name = $value; + } + } + + /** + * Returns the public member variables of an object. + * This method is provided such that we can get the public member variables of an object. + * It is different from "get_object_vars()" because the latter will return private + * and protected variables if it is called within the object itself. + * @param object $object the object to be handled + * @return array the public member variables of the object + */ + public static function getObjectVars($object) + { + return get_object_vars($object); + } +} diff --git a/framework/yii/Yii.php b/framework/yii/Yii.php index bde15cc..0406463 100644 --- a/framework/yii/Yii.php +++ b/framework/yii/Yii.php @@ -12,13 +12,13 @@ require(__DIR__ . '/YiiBase.php'); /** * Yii is a helper class serving common framework functionalities. * - * It extends from [[YiiBase]] which provides the actual implementation. - * By writing your own Yii class, you can customize some functionalities of [[YiiBase]]. + * It extends from [[AbstractYii]] which provides the actual implementation. + * By writing your own Yii class, you can customize some functionalities of [[AbstractYii]]. * * @author Qiang Xue * @since 2.0 */ -class Yii extends \yii\YiiBase +class Yii extends \yii\AbstractYii { } diff --git a/framework/yii/YiiBase.php b/framework/yii/YiiBase.php deleted file mode 100644 index f04903d..0000000 --- a/framework/yii/YiiBase.php +++ /dev/null @@ -1,574 +0,0 @@ - - * @since 2.0 - */ -class YiiBase -{ - /** - * @var array class map used by the Yii autoloading mechanism. - * The array keys are the class names (without leading backslashes), and the array values - * are the corresponding class file paths (or path aliases). This property mainly affects - * how [[autoload()]] works. - * @see import - * @see autoload - */ - public static $classMap = array(); - /** - * @var \yii\console\Application|\yii\web\Application the application instance - */ - public static $app; - /** - * @var array registered path aliases - * @see getAlias - * @see setAlias - */ - public static $aliases = array('@yii' => __DIR__); - /** - * @var array initial property values that will be applied to objects newly created via [[createObject]]. - * The array keys are class names without leading backslashes "\", and the array values are the corresponding - * name-value pairs for initializing the created class instances. For example, - * - * ~~~ - * array( - * 'Bar' => array( - * 'prop1' => 'value1', - * 'prop2' => 'value2', - * ), - * 'mycompany\foo\Car' => array( - * 'prop1' => 'value1', - * 'prop2' => 'value2', - * ), - * ) - * ~~~ - * - * @see createObject - */ - public static $objectConfig = array(); - - - /** - * @return string the version of Yii framework - */ - public static function getVersion() - { - return '2.0-dev'; - } - - /** - * Imports a set of namespaces. - * - * By importing a namespace, the method will create an alias for the directory corresponding - * to the namespace. For example, if "foo\bar" is a namespace associated with the directory - * "path/to/foo/bar", then an alias "@foo/bar" will be created for this directory. - * - * This method is typically invoked in the bootstrap file to import the namespaces of - * the installed extensions. By default, Composer, when installing new extensions, will - * generate such a mapping file which can be loaded and passed to this method. - * - * @param array $namespaces the namespaces to be imported. The keys are the namespaces, - * and the values are the corresponding directories. - */ - public static function importNamespaces($namespaces) - { - foreach ($namespaces as $name => $path) { - if ($name !== '') { - $name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/'); - if (is_array($path)) { - $path = reset($path); - } - static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name); - } - } - } - - /** - * Translates a path alias into an actual path. - * - * The translation is done according to the following procedure: - * - * 1. If the given alias does not start with '@', it is returned back without change; - * 2. Otherwise, look for the longest registered alias that matches the beginning part - * of the given alias. If it exists, replace the matching part of the given alias with - * the corresponding registered path. - * 3. Throw an exception or return false, depending on the `$throwException` parameter. - * - * For example, by default '@yii' is registered as the alias to the Yii framework directory, - * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'. - * - * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config' - * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path. - * This is because the longest alias takes precedence. - * - * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced - * instead of '@foo/bar', because '/' serves as the boundary character. - * - * Note, this method does not check if the returned path exists or not. - * - * @param string $alias the alias to be translated. - * @param boolean $throwException whether to throw an exception if the given alias is invalid. - * If this is false and an invalid alias is given, false will be returned by this method. - * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered. - * @throws InvalidParamException if the alias is invalid while $throwException is true. - * @see setAlias - */ - public static function getAlias($alias, $throwException = true) - { - if (strncmp($alias, '@', 1)) { - // not an alias - return $alias; - } - - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - - if (isset(self::$aliases[$root])) { - if (is_string(self::$aliases[$root])) { - return $pos === false ? self::$aliases[$root] : self::$aliases[$root] . substr($alias, $pos); - } else { - foreach (self::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $path . substr($alias, strlen($name)); - } - } - } - } - - if ($throwException) { - throw new InvalidParamException("Invalid path alias: $alias"); - } else { - return false; - } - } - - /** - * Returns the root alias part of a given alias. - * A root alias is an alias that has been registered via [[setAlias()]] previously. - * If a given alias matches multiple root aliases, the longest one will be returned. - * @param string $alias the alias - * @return string|boolean the root alias, or false if no root alias is found - */ - public static function getRootAlias($alias) - { - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - - if (isset(self::$aliases[$root])) { - if (is_string(self::$aliases[$root])) { - return $root; - } else { - foreach (self::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $name; - } - } - } - } - return false; - } - - /** - * Registers a path alias. - * - * A path alias is a short name representing a long path (a file path, a URL, etc.) - * For example, we use '@yii' as the alias of the path to the Yii framework directory. - * - * A path alias must start with the character '@' so that it can be easily differentiated - * from non-alias paths. - * - * Note that this method does not check if the given path exists or not. All it does is - * to associate the alias with the path. - * - * Any trailing '/' and '\' characters in the given path will be trimmed. - * - * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character. - * It may contain the forward slash '/' which serves as boundary character when performing - * alias translation by [[getAlias()]]. - * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters - * will be trimmed. This can be - * - * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`) - * - a URL (e.g. `http://www.yiiframework.com`) - * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the - * actual path first by calling [[getAlias()]]. - * - * @throws InvalidParamException if $path is an invalid alias. - * @see getAlias - */ - public static function setAlias($alias, $path) - { - if (strncmp($alias, '@', 1)) { - $alias = '@' . $alias; - } - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - if ($path !== null) { - $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); - if (!isset(self::$aliases[$root])) { - if ($pos === false) { - self::$aliases[$root] = $path; - } else { - self::$aliases[$root] = array($alias => $path); - } - } elseif (is_string(self::$aliases[$root])) { - if ($pos === false) { - self::$aliases[$root] = $path; - } else { - self::$aliases[$root] = array( - $alias => $path, - $root => self::$aliases[$root], - ); - } - } else { - self::$aliases[$root][$alias] = $path; - krsort(self::$aliases[$root]); - } - } elseif (isset(self::$aliases[$root])) { - if (is_array(self::$aliases[$root])) { - unset(self::$aliases[$root][$alias]); - } elseif ($pos === false) { - unset(self::$aliases[$root]); - } - } - } - - /** - * Class autoload loader. - * This method is invoked automatically when PHP sees an unknown class. - * The method will attempt to include the class file according to the following procedure: - * - * 1. Search in [[classMap]]; - * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt - * to include the file associated with the corresponding path alias - * (e.g. `@yii/base/Component.php`); - * 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`), - * it will attempt to include the file associated with the corresponding path alias - * (e.g. `@PHPUnit/Framework/TestCase.php`); - * - * This autoloader allows loading classes that follow the [PSR-0 standard](http://www.php-fig.org/psr/0/). - * Therefor a path alias has to be defined for each top-level namespace. - * - * @param string $className the fully qualified class name without a leading backslash "\" - * @throws UnknownClassException if the class does not exist in the class file - */ - public static function autoload($className) - { - if (isset(self::$classMap[$className])) { - $classFile = self::$classMap[$className]; - if ($classFile[0] === '@') { - $classFile = static::getAlias($classFile); - } - } else { - // follow PSR-0 to determine the class file - if (($pos = strrpos($className, '\\')) !== false) { - // namespaced class, e.g. yii\base\Component - $path = str_replace('\\', '/', substr($className, 0, $pos + 1)) - . str_replace('_', '/', substr($className, $pos + 1)) . '.php'; - } else { - $path = str_replace('_', '/', $className) . '.php'; - } - - // try loading via path alias - if (strpos($path, '/') === false) { - return; - } else { - $classFile = static::getAlias('@' . $path, false); - if ($classFile === false || !is_file($classFile)) { - return; - } - } - } - - include($classFile); - - if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && - (!function_exists('trait_exists') || !trait_exists($className, false))) { - throw new UnknownClassException("Unable to find '$className' in file: $classFile"); - } - } - - /** - * Creates a new object using the given configuration. - * - * The configuration can be either a string or an array. - * If a string, it is treated as the *object class*; if an array, - * it must contain a `class` element specifying the *object class*, and - * the rest of the name-value pairs in the array will be used to initialize - * the corresponding object properties. - * - * The object type can be either a class name or the [[getAlias()|alias]] of - * the class. For example, - * - * - `app\components\GoogleMap`: fully-qualified namespaced class. - * - `@app/components/GoogleMap`: an alias, used for non-namespaced class. - * - * Below are some usage examples: - * - * ~~~ - * $object = \Yii::createObject('@app/components/GoogleMap'); - * $object = \Yii::createObject(array( - * 'class' => '\app\components\GoogleMap', - * 'apiKey' => 'xyz', - * )); - * ~~~ - * - * This method can be used to create any object as long as the object's constructor is - * defined like the following: - * - * ~~~ - * public function __construct(..., $config = array()) { - * } - * ~~~ - * - * The method will pass the given configuration as the last parameter of the constructor, - * and any additional parameters to this method will be passed as the rest of the constructor parameters. - * - * @param string|array $config the configuration. It can be either a string representing the class name - * or an array representing the object configuration. - * @return mixed the created object - * @throws InvalidConfigException if the configuration is invalid. - */ - public static function createObject($config) - { - static $reflections = array(); - - if (is_string($config)) { - $class = $config; - $config = array(); - } elseif (isset($config['class'])) { - $class = $config['class']; - unset($config['class']); - } else { - throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); - } - - $class = ltrim($class, '\\'); - - if (isset(self::$objectConfig[$class])) { - $config = array_merge(self::$objectConfig[$class], $config); - } - - if (($n = func_num_args()) > 1) { - /** @var $reflection \ReflectionClass */ - if (isset($reflections[$class])) { - $reflection = $reflections[$class]; - } else { - $reflection = $reflections[$class] = new \ReflectionClass($class); - } - $args = func_get_args(); - array_shift($args); // remove $config - if (!empty($config)) { - $args[] = $config; - } - return $reflection->newInstanceArgs($args); - } else { - return empty($config) ? new $class : new $class($config); - } - } - - /** - * Logs a trace message. - * Trace messages are logged mainly for development purpose to see - * the execution work flow of some code. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function trace($message, $category = 'application') - { - if (YII_DEBUG) { - self::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category); - } - } - - /** - * Logs an error message. - * An error message is typically logged when an unrecoverable error occurs - * during the execution of an application. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function error($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category); - } - - /** - * Logs a warning message. - * A warning message is typically logged when an error occurs while the execution - * can still continue. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function warning($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category); - } - - /** - * Logs an informative message. - * An informative message is typically logged by an application to keep record of - * something important (e.g. an administrator logs in). - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function info($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_INFO, $category); - } - - /** - * Marks the beginning of a code block for profiling. - * This has to be matched with a call to [[endProfile]] with the same category name. - * The begin- and end- calls must also be properly nested. For example, - * - * ~~~ - * \Yii::beginProfile('block1'); - * // some code to be profiled - * \Yii::beginProfile('block2'); - * // some other code to be profiled - * \Yii::endProfile('block2'); - * \Yii::endProfile('block1'); - * ~~~ - * @param string $token token for the code block - * @param string $category the category of this log message - * @see endProfile - */ - public static function beginProfile($token, $category = 'application') - { - self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); - } - - /** - * Marks the end of a code block for profiling. - * This has to be matched with a previous call to [[beginProfile]] with the same category name. - * @param string $token token for the code block - * @param string $category the category of this log message - * @see beginProfile - */ - public static function endProfile($token, $category = 'application') - { - self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category); - } - - /** - * Returns an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information. - * @return string an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information - */ - public static function powered() - { - return 'Powered by Yii Framework'; - } - - /** - * Translates a message to the specified language. - * - * This is a shortcut method of [[\yii\i18n\I18N::translate()]]. - * - * The translation will be conducted according to the message category and the target language will be used. - * - * In case when a translated message has different plural forms (separated by "|"), this method - * will also attempt to choose an appropriate one according to a given numeric value which is - * specified as the first parameter (indexed by 0) in `$params`. - * - * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first - * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}" - * will be replaced with the given number. - * - * For more details on how plural rules are applied, please refer to: - * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]] - * - * @param string $category the message category. - * @param string $message the message to be translated. - * @param array $params the parameters that will be used to replace the corresponding placeholders in the message. - * @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current - * [[\yii\base\Application::language|application language]] will be used. - * @return string the translated message. - */ - public static function t($category, $message, $params = array(), $language = null) - { - if (self::$app !== null) { - return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language); - } else { - return is_array($params) ? strtr($message, $params) : $message; - } - } - - /** - * Configures an object with the initial property values. - * @param object $object the object to be configured - * @param array $properties the property initial values given in terms of name-value pairs. - */ - public static function configure($object, $properties) - { - foreach ($properties as $name => $value) { - $object->$name = $value; - } - } - - /** - * Returns the public member variables of an object. - * This method is provided such that we can get the public member variables of an object. - * It is different from "get_object_vars()" because the latter will return private - * and protected variables if it is called within the object itself. - * @param object $object the object to be handled - * @return array the public member variables of the object - */ - public static function getObjectVars($object) - { - return get_object_vars($object); - } -} diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php index 54f3a49..76354d4 100644 --- a/framework/yii/log/Logger.php +++ b/framework/yii/log/Logger.php @@ -220,7 +220,7 @@ class Logger extends Component * Returns the total elapsed time since the start of the current request. * This method calculates the difference between now and the timestamp * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning - * of [[YiiBase]] class file. + * of [[AbstractYii]] class file. * @return float the total elapsed time in seconds for current request. */ public function getElapsedTime() From b873f9f242b3b927fcc9fe9b34ab1ec5e48f69d0 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 28 Sep 2013 16:25:43 +0400 Subject: [PATCH 4/4] Fixes #915: helper classes renamed again --- build/build.xml | 2 +- build/controllers/ClassmapController.php | 2 +- build/controllers/PhpDocController.php | 2 +- docs/internals/autoloader.md | 2 +- framework/yii/AbstractYii.php | 574 --------- framework/yii/BaseYii.php | 574 +++++++++ framework/yii/Yii.php | 8 +- framework/yii/classes.php | 22 +- framework/yii/helpers/AbstractArrayHelper.php | 451 ------- framework/yii/helpers/AbstractConsole.php | 835 ------------- framework/yii/helpers/AbstractFileHelper.php | 329 ----- framework/yii/helpers/AbstractHtml.php | 1599 ------------------------ framework/yii/helpers/AbstractHtmlPurifier.php | 34 - framework/yii/helpers/AbstractInflector.php | 480 ------- framework/yii/helpers/AbstractJson.php | 112 -- framework/yii/helpers/AbstractMarkdown.php | 44 - framework/yii/helpers/AbstractSecurity.php | 285 ----- framework/yii/helpers/AbstractStringHelper.php | 138 -- framework/yii/helpers/AbstractVarDumper.php | 127 -- framework/yii/helpers/ArrayHelper.php | 2 +- framework/yii/helpers/BaseArrayHelper.php | 451 +++++++ framework/yii/helpers/BaseConsole.php | 835 +++++++++++++ framework/yii/helpers/BaseFileHelper.php | 329 +++++ framework/yii/helpers/BaseHtml.php | 1599 ++++++++++++++++++++++++ framework/yii/helpers/BaseHtmlPurifier.php | 34 + framework/yii/helpers/BaseInflector.php | 480 +++++++ framework/yii/helpers/BaseJson.php | 112 ++ framework/yii/helpers/BaseMarkdown.php | 44 + framework/yii/helpers/BaseSecurity.php | 285 +++++ framework/yii/helpers/BaseStringHelper.php | 138 ++ framework/yii/helpers/BaseVarDumper.php | 127 ++ framework/yii/helpers/Console.php | 2 +- framework/yii/helpers/FileHelper.php | 2 +- framework/yii/helpers/Html.php | 2 +- framework/yii/helpers/HtmlPurifier.php | 2 +- framework/yii/helpers/Inflector.php | 2 +- framework/yii/helpers/Json.php | 2 +- framework/yii/helpers/Markdown.php | 2 +- framework/yii/helpers/Security.php | 2 +- framework/yii/helpers/StringHelper.php | 2 +- framework/yii/helpers/VarDumper.php | 2 +- framework/yii/log/Logger.php | 2 +- 42 files changed, 5039 insertions(+), 5039 deletions(-) delete mode 100644 framework/yii/AbstractYii.php create mode 100644 framework/yii/BaseYii.php delete mode 100644 framework/yii/helpers/AbstractArrayHelper.php delete mode 100644 framework/yii/helpers/AbstractConsole.php delete mode 100644 framework/yii/helpers/AbstractFileHelper.php delete mode 100644 framework/yii/helpers/AbstractHtml.php delete mode 100644 framework/yii/helpers/AbstractHtmlPurifier.php delete mode 100644 framework/yii/helpers/AbstractInflector.php delete mode 100644 framework/yii/helpers/AbstractJson.php delete mode 100644 framework/yii/helpers/AbstractMarkdown.php delete mode 100644 framework/yii/helpers/AbstractSecurity.php delete mode 100644 framework/yii/helpers/AbstractStringHelper.php delete mode 100644 framework/yii/helpers/AbstractVarDumper.php create mode 100644 framework/yii/helpers/BaseArrayHelper.php create mode 100644 framework/yii/helpers/BaseConsole.php create mode 100644 framework/yii/helpers/BaseFileHelper.php create mode 100644 framework/yii/helpers/BaseHtml.php create mode 100644 framework/yii/helpers/BaseHtmlPurifier.php create mode 100644 framework/yii/helpers/BaseInflector.php create mode 100644 framework/yii/helpers/BaseJson.php create mode 100644 framework/yii/helpers/BaseMarkdown.php create mode 100644 framework/yii/helpers/BaseSecurity.php create mode 100644 framework/yii/helpers/BaseStringHelper.php create mode 100644 framework/yii/helpers/BaseVarDumper.php diff --git a/build/build.xml b/build/build.xml index 85d5aa5..b0975dc 100644 --- a/build/build.xml +++ b/build/build.xml @@ -265,7 +265,7 @@ Please update yiisite/common/data/versions.php file with the following code: where <target name> can be one of the following: - - sync : synchronize yiilite.php and AbstractYii.php + - sync : synchronize yiilite.php and BaseYii.php - message : extract i18n messages of the framework - src : build source release - doc : build documentation release (Windows only) diff --git a/build/controllers/ClassmapController.php b/build/controllers/ClassmapController.php index 10eb134..6a5ac8c 100644 --- a/build/controllers/ClassmapController.php +++ b/build/controllers/ClassmapController.php @@ -45,7 +45,7 @@ class ClassmapController extends Controller 'only' => array('.php'), 'except' => array( 'Yii.php', - 'AbstractYii.php', + 'BaseYii.php', '/debug/', '/console/', '/test/', diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 4fed3b2..cb574ff 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -56,7 +56,7 @@ class PhpDocController extends Controller }, 'only' => array('.php'), 'except' => array( - 'AbstractYii.php', + 'BaseYii.php', 'Yii.php', '/debug/views/', '/requirements/', diff --git a/docs/internals/autoloader.md b/docs/internals/autoloader.md index 067773d..76a545b 100644 --- a/docs/internals/autoloader.md +++ b/docs/internals/autoloader.md @@ -16,4 +16,4 @@ PEAR-style libraries References ---------- -- AbstractYii::autoload \ No newline at end of file +- BaseYii::autoload \ No newline at end of file diff --git a/framework/yii/AbstractYii.php b/framework/yii/AbstractYii.php deleted file mode 100644 index c40aad9..0000000 --- a/framework/yii/AbstractYii.php +++ /dev/null @@ -1,574 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractYii -{ - /** - * @var array class map used by the Yii autoloading mechanism. - * The array keys are the class names (without leading backslashes), and the array values - * are the corresponding class file paths (or path aliases). This property mainly affects - * how [[autoload()]] works. - * @see import - * @see autoload - */ - public static $classMap = array(); - /** - * @var \yii\console\Application|\yii\web\Application the application instance - */ - public static $app; - /** - * @var array registered path aliases - * @see getAlias - * @see setAlias - */ - public static $aliases = array('@yii' => __DIR__); - /** - * @var array initial property values that will be applied to objects newly created via [[createObject]]. - * The array keys are class names without leading backslashes "\", and the array values are the corresponding - * name-value pairs for initializing the created class instances. For example, - * - * ~~~ - * array( - * 'Bar' => array( - * 'prop1' => 'value1', - * 'prop2' => 'value2', - * ), - * 'mycompany\foo\Car' => array( - * 'prop1' => 'value1', - * 'prop2' => 'value2', - * ), - * ) - * ~~~ - * - * @see createObject - */ - public static $objectConfig = array(); - - - /** - * @return string the version of Yii framework - */ - public static function getVersion() - { - return '2.0-dev'; - } - - /** - * Imports a set of namespaces. - * - * By importing a namespace, the method will create an alias for the directory corresponding - * to the namespace. For example, if "foo\bar" is a namespace associated with the directory - * "path/to/foo/bar", then an alias "@foo/bar" will be created for this directory. - * - * This method is typically invoked in the bootstrap file to import the namespaces of - * the installed extensions. By default, Composer, when installing new extensions, will - * generate such a mapping file which can be loaded and passed to this method. - * - * @param array $namespaces the namespaces to be imported. The keys are the namespaces, - * and the values are the corresponding directories. - */ - public static function importNamespaces($namespaces) - { - foreach ($namespaces as $name => $path) { - if ($name !== '') { - $name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/'); - if (is_array($path)) { - $path = reset($path); - } - static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name); - } - } - } - - /** - * Translates a path alias into an actual path. - * - * The translation is done according to the following procedure: - * - * 1. If the given alias does not start with '@', it is returned back without change; - * 2. Otherwise, look for the longest registered alias that matches the beginning part - * of the given alias. If it exists, replace the matching part of the given alias with - * the corresponding registered path. - * 3. Throw an exception or return false, depending on the `$throwException` parameter. - * - * For example, by default '@yii' is registered as the alias to the Yii framework directory, - * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'. - * - * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config' - * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path. - * This is because the longest alias takes precedence. - * - * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced - * instead of '@foo/bar', because '/' serves as the boundary character. - * - * Note, this method does not check if the returned path exists or not. - * - * @param string $alias the alias to be translated. - * @param boolean $throwException whether to throw an exception if the given alias is invalid. - * If this is false and an invalid alias is given, false will be returned by this method. - * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered. - * @throws InvalidParamException if the alias is invalid while $throwException is true. - * @see setAlias - */ - public static function getAlias($alias, $throwException = true) - { - if (strncmp($alias, '@', 1)) { - // not an alias - return $alias; - } - - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - - if (isset(self::$aliases[$root])) { - if (is_string(self::$aliases[$root])) { - return $pos === false ? self::$aliases[$root] : self::$aliases[$root] . substr($alias, $pos); - } else { - foreach (self::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $path . substr($alias, strlen($name)); - } - } - } - } - - if ($throwException) { - throw new InvalidParamException("Invalid path alias: $alias"); - } else { - return false; - } - } - - /** - * Returns the root alias part of a given alias. - * A root alias is an alias that has been registered via [[setAlias()]] previously. - * If a given alias matches multiple root aliases, the longest one will be returned. - * @param string $alias the alias - * @return string|boolean the root alias, or false if no root alias is found - */ - public static function getRootAlias($alias) - { - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - - if (isset(self::$aliases[$root])) { - if (is_string(self::$aliases[$root])) { - return $root; - } else { - foreach (self::$aliases[$root] as $name => $path) { - if (strpos($alias . '/', $name . '/') === 0) { - return $name; - } - } - } - } - return false; - } - - /** - * Registers a path alias. - * - * A path alias is a short name representing a long path (a file path, a URL, etc.) - * For example, we use '@yii' as the alias of the path to the Yii framework directory. - * - * A path alias must start with the character '@' so that it can be easily differentiated - * from non-alias paths. - * - * Note that this method does not check if the given path exists or not. All it does is - * to associate the alias with the path. - * - * Any trailing '/' and '\' characters in the given path will be trimmed. - * - * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character. - * It may contain the forward slash '/' which serves as boundary character when performing - * alias translation by [[getAlias()]]. - * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters - * will be trimmed. This can be - * - * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`) - * - a URL (e.g. `http://www.yiiframework.com`) - * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the - * actual path first by calling [[getAlias()]]. - * - * @throws InvalidParamException if $path is an invalid alias. - * @see getAlias - */ - public static function setAlias($alias, $path) - { - if (strncmp($alias, '@', 1)) { - $alias = '@' . $alias; - } - $pos = strpos($alias, '/'); - $root = $pos === false ? $alias : substr($alias, 0, $pos); - if ($path !== null) { - $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); - if (!isset(self::$aliases[$root])) { - if ($pos === false) { - self::$aliases[$root] = $path; - } else { - self::$aliases[$root] = array($alias => $path); - } - } elseif (is_string(self::$aliases[$root])) { - if ($pos === false) { - self::$aliases[$root] = $path; - } else { - self::$aliases[$root] = array( - $alias => $path, - $root => self::$aliases[$root], - ); - } - } else { - self::$aliases[$root][$alias] = $path; - krsort(self::$aliases[$root]); - } - } elseif (isset(self::$aliases[$root])) { - if (is_array(self::$aliases[$root])) { - unset(self::$aliases[$root][$alias]); - } elseif ($pos === false) { - unset(self::$aliases[$root]); - } - } - } - - /** - * Class autoload loader. - * This method is invoked automatically when PHP sees an unknown class. - * The method will attempt to include the class file according to the following procedure: - * - * 1. Search in [[classMap]]; - * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt - * to include the file associated with the corresponding path alias - * (e.g. `@yii/base/Component.php`); - * 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`), - * it will attempt to include the file associated with the corresponding path alias - * (e.g. `@PHPUnit/Framework/TestCase.php`); - * - * This autoloader allows loading classes that follow the [PSR-0 standard](http://www.php-fig.org/psr/0/). - * Therefor a path alias has to be defined for each top-level namespace. - * - * @param string $className the fully qualified class name without a leading backslash "\" - * @throws UnknownClassException if the class does not exist in the class file - */ - public static function autoload($className) - { - if (isset(self::$classMap[$className])) { - $classFile = self::$classMap[$className]; - if ($classFile[0] === '@') { - $classFile = static::getAlias($classFile); - } - } else { - // follow PSR-0 to determine the class file - if (($pos = strrpos($className, '\\')) !== false) { - // namespaced class, e.g. yii\base\Component - $path = str_replace('\\', '/', substr($className, 0, $pos + 1)) - . str_replace('_', '/', substr($className, $pos + 1)) . '.php'; - } else { - $path = str_replace('_', '/', $className) . '.php'; - } - - // try loading via path alias - if (strpos($path, '/') === false) { - return; - } else { - $classFile = static::getAlias('@' . $path, false); - if ($classFile === false || !is_file($classFile)) { - return; - } - } - } - - include($classFile); - - if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && - (!function_exists('trait_exists') || !trait_exists($className, false))) { - throw new UnknownClassException("Unable to find '$className' in file: $classFile"); - } - } - - /** - * Creates a new object using the given configuration. - * - * The configuration can be either a string or an array. - * If a string, it is treated as the *object class*; if an array, - * it must contain a `class` element specifying the *object class*, and - * the rest of the name-value pairs in the array will be used to initialize - * the corresponding object properties. - * - * The object type can be either a class name or the [[getAlias()|alias]] of - * the class. For example, - * - * - `app\components\GoogleMap`: fully-qualified namespaced class. - * - `@app/components/GoogleMap`: an alias, used for non-namespaced class. - * - * Below are some usage examples: - * - * ~~~ - * $object = \Yii::createObject('@app/components/GoogleMap'); - * $object = \Yii::createObject(array( - * 'class' => '\app\components\GoogleMap', - * 'apiKey' => 'xyz', - * )); - * ~~~ - * - * This method can be used to create any object as long as the object's constructor is - * defined like the following: - * - * ~~~ - * public function __construct(..., $config = array()) { - * } - * ~~~ - * - * The method will pass the given configuration as the last parameter of the constructor, - * and any additional parameters to this method will be passed as the rest of the constructor parameters. - * - * @param string|array $config the configuration. It can be either a string representing the class name - * or an array representing the object configuration. - * @return mixed the created object - * @throws InvalidConfigException if the configuration is invalid. - */ - public static function createObject($config) - { - static $reflections = array(); - - if (is_string($config)) { - $class = $config; - $config = array(); - } elseif (isset($config['class'])) { - $class = $config['class']; - unset($config['class']); - } else { - throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); - } - - $class = ltrim($class, '\\'); - - if (isset(self::$objectConfig[$class])) { - $config = array_merge(self::$objectConfig[$class], $config); - } - - if (($n = func_num_args()) > 1) { - /** @var $reflection \ReflectionClass */ - if (isset($reflections[$class])) { - $reflection = $reflections[$class]; - } else { - $reflection = $reflections[$class] = new \ReflectionClass($class); - } - $args = func_get_args(); - array_shift($args); // remove $config - if (!empty($config)) { - $args[] = $config; - } - return $reflection->newInstanceArgs($args); - } else { - return empty($config) ? new $class : new $class($config); - } - } - - /** - * Logs a trace message. - * Trace messages are logged mainly for development purpose to see - * the execution work flow of some code. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function trace($message, $category = 'application') - { - if (YII_DEBUG) { - self::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category); - } - } - - /** - * Logs an error message. - * An error message is typically logged when an unrecoverable error occurs - * during the execution of an application. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function error($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category); - } - - /** - * Logs a warning message. - * A warning message is typically logged when an error occurs while the execution - * can still continue. - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function warning($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category); - } - - /** - * Logs an informative message. - * An informative message is typically logged by an application to keep record of - * something important (e.g. an administrator logs in). - * @param string $message the message to be logged. - * @param string $category the category of the message. - */ - public static function info($message, $category = 'application') - { - self::$app->getLog()->log($message, Logger::LEVEL_INFO, $category); - } - - /** - * Marks the beginning of a code block for profiling. - * This has to be matched with a call to [[endProfile]] with the same category name. - * The begin- and end- calls must also be properly nested. For example, - * - * ~~~ - * \Yii::beginProfile('block1'); - * // some code to be profiled - * \Yii::beginProfile('block2'); - * // some other code to be profiled - * \Yii::endProfile('block2'); - * \Yii::endProfile('block1'); - * ~~~ - * @param string $token token for the code block - * @param string $category the category of this log message - * @see endProfile - */ - public static function beginProfile($token, $category = 'application') - { - self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); - } - - /** - * Marks the end of a code block for profiling. - * This has to be matched with a previous call to [[beginProfile]] with the same category name. - * @param string $token token for the code block - * @param string $category the category of this log message - * @see beginProfile - */ - public static function endProfile($token, $category = 'application') - { - self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category); - } - - /** - * Returns an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information. - * @return string an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information - */ - public static function powered() - { - return 'Powered by Yii Framework'; - } - - /** - * Translates a message to the specified language. - * - * This is a shortcut method of [[\yii\i18n\I18N::translate()]]. - * - * The translation will be conducted according to the message category and the target language will be used. - * - * In case when a translated message has different plural forms (separated by "|"), this method - * will also attempt to choose an appropriate one according to a given numeric value which is - * specified as the first parameter (indexed by 0) in `$params`. - * - * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first - * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}" - * will be replaced with the given number. - * - * For more details on how plural rules are applied, please refer to: - * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]] - * - * @param string $category the message category. - * @param string $message the message to be translated. - * @param array $params the parameters that will be used to replace the corresponding placeholders in the message. - * @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current - * [[\yii\base\Application::language|application language]] will be used. - * @return string the translated message. - */ - public static function t($category, $message, $params = array(), $language = null) - { - if (self::$app !== null) { - return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language); - } else { - return is_array($params) ? strtr($message, $params) : $message; - } - } - - /** - * Configures an object with the initial property values. - * @param object $object the object to be configured - * @param array $properties the property initial values given in terms of name-value pairs. - */ - public static function configure($object, $properties) - { - foreach ($properties as $name => $value) { - $object->$name = $value; - } - } - - /** - * Returns the public member variables of an object. - * This method is provided such that we can get the public member variables of an object. - * It is different from "get_object_vars()" because the latter will return private - * and protected variables if it is called within the object itself. - * @param object $object the object to be handled - * @return array the public member variables of the object - */ - public static function getObjectVars($object) - { - return get_object_vars($object); - } -} diff --git a/framework/yii/BaseYii.php b/framework/yii/BaseYii.php new file mode 100644 index 0000000..b586160 --- /dev/null +++ b/framework/yii/BaseYii.php @@ -0,0 +1,574 @@ + + * @since 2.0 + */ +class BaseYii +{ + /** + * @var array class map used by the Yii autoloading mechanism. + * The array keys are the class names (without leading backslashes), and the array values + * are the corresponding class file paths (or path aliases). This property mainly affects + * how [[autoload()]] works. + * @see import + * @see autoload + */ + public static $classMap = array(); + /** + * @var \yii\console\Application|\yii\web\Application the application instance + */ + public static $app; + /** + * @var array registered path aliases + * @see getAlias + * @see setAlias + */ + public static $aliases = array('@yii' => __DIR__); + /** + * @var array initial property values that will be applied to objects newly created via [[createObject]]. + * The array keys are class names without leading backslashes "\", and the array values are the corresponding + * name-value pairs for initializing the created class instances. For example, + * + * ~~~ + * array( + * 'Bar' => array( + * 'prop1' => 'value1', + * 'prop2' => 'value2', + * ), + * 'mycompany\foo\Car' => array( + * 'prop1' => 'value1', + * 'prop2' => 'value2', + * ), + * ) + * ~~~ + * + * @see createObject + */ + public static $objectConfig = array(); + + + /** + * @return string the version of Yii framework + */ + public static function getVersion() + { + return '2.0-dev'; + } + + /** + * Imports a set of namespaces. + * + * By importing a namespace, the method will create an alias for the directory corresponding + * to the namespace. For example, if "foo\bar" is a namespace associated with the directory + * "path/to/foo/bar", then an alias "@foo/bar" will be created for this directory. + * + * This method is typically invoked in the bootstrap file to import the namespaces of + * the installed extensions. By default, Composer, when installing new extensions, will + * generate such a mapping file which can be loaded and passed to this method. + * + * @param array $namespaces the namespaces to be imported. The keys are the namespaces, + * and the values are the corresponding directories. + */ + public static function importNamespaces($namespaces) + { + foreach ($namespaces as $name => $path) { + if ($name !== '') { + $name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/'); + if (is_array($path)) { + $path = reset($path); + } + static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name); + } + } + } + + /** + * Translates a path alias into an actual path. + * + * The translation is done according to the following procedure: + * + * 1. If the given alias does not start with '@', it is returned back without change; + * 2. Otherwise, look for the longest registered alias that matches the beginning part + * of the given alias. If it exists, replace the matching part of the given alias with + * the corresponding registered path. + * 3. Throw an exception or return false, depending on the `$throwException` parameter. + * + * For example, by default '@yii' is registered as the alias to the Yii framework directory, + * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'. + * + * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config' + * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path. + * This is because the longest alias takes precedence. + * + * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced + * instead of '@foo/bar', because '/' serves as the boundary character. + * + * Note, this method does not check if the returned path exists or not. + * + * @param string $alias the alias to be translated. + * @param boolean $throwException whether to throw an exception if the given alias is invalid. + * If this is false and an invalid alias is given, false will be returned by this method. + * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered. + * @throws InvalidParamException if the alias is invalid while $throwException is true. + * @see setAlias + */ + public static function getAlias($alias, $throwException = true) + { + if (strncmp($alias, '@', 1)) { + // not an alias + return $alias; + } + + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + + if (isset(self::$aliases[$root])) { + if (is_string(self::$aliases[$root])) { + return $pos === false ? self::$aliases[$root] : self::$aliases[$root] . substr($alias, $pos); + } else { + foreach (self::$aliases[$root] as $name => $path) { + if (strpos($alias . '/', $name . '/') === 0) { + return $path . substr($alias, strlen($name)); + } + } + } + } + + if ($throwException) { + throw new InvalidParamException("Invalid path alias: $alias"); + } else { + return false; + } + } + + /** + * Returns the root alias part of a given alias. + * A root alias is an alias that has been registered via [[setAlias()]] previously. + * If a given alias matches multiple root aliases, the longest one will be returned. + * @param string $alias the alias + * @return string|boolean the root alias, or false if no root alias is found + */ + public static function getRootAlias($alias) + { + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + + if (isset(self::$aliases[$root])) { + if (is_string(self::$aliases[$root])) { + return $root; + } else { + foreach (self::$aliases[$root] as $name => $path) { + if (strpos($alias . '/', $name . '/') === 0) { + return $name; + } + } + } + } + return false; + } + + /** + * Registers a path alias. + * + * A path alias is a short name representing a long path (a file path, a URL, etc.) + * For example, we use '@yii' as the alias of the path to the Yii framework directory. + * + * A path alias must start with the character '@' so that it can be easily differentiated + * from non-alias paths. + * + * Note that this method does not check if the given path exists or not. All it does is + * to associate the alias with the path. + * + * Any trailing '/' and '\' characters in the given path will be trimmed. + * + * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character. + * It may contain the forward slash '/' which serves as boundary character when performing + * alias translation by [[getAlias()]]. + * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters + * will be trimmed. This can be + * + * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`) + * - a URL (e.g. `http://www.yiiframework.com`) + * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the + * actual path first by calling [[getAlias()]]. + * + * @throws InvalidParamException if $path is an invalid alias. + * @see getAlias + */ + public static function setAlias($alias, $path) + { + if (strncmp($alias, '@', 1)) { + $alias = '@' . $alias; + } + $pos = strpos($alias, '/'); + $root = $pos === false ? $alias : substr($alias, 0, $pos); + if ($path !== null) { + $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); + if (!isset(self::$aliases[$root])) { + if ($pos === false) { + self::$aliases[$root] = $path; + } else { + self::$aliases[$root] = array($alias => $path); + } + } elseif (is_string(self::$aliases[$root])) { + if ($pos === false) { + self::$aliases[$root] = $path; + } else { + self::$aliases[$root] = array( + $alias => $path, + $root => self::$aliases[$root], + ); + } + } else { + self::$aliases[$root][$alias] = $path; + krsort(self::$aliases[$root]); + } + } elseif (isset(self::$aliases[$root])) { + if (is_array(self::$aliases[$root])) { + unset(self::$aliases[$root][$alias]); + } elseif ($pos === false) { + unset(self::$aliases[$root]); + } + } + } + + /** + * Class autoload loader. + * This method is invoked automatically when PHP sees an unknown class. + * The method will attempt to include the class file according to the following procedure: + * + * 1. Search in [[classMap]]; + * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt + * to include the file associated with the corresponding path alias + * (e.g. `@yii/base/Component.php`); + * 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`), + * it will attempt to include the file associated with the corresponding path alias + * (e.g. `@PHPUnit/Framework/TestCase.php`); + * + * This autoloader allows loading classes that follow the [PSR-0 standard](http://www.php-fig.org/psr/0/). + * Therefor a path alias has to be defined for each top-level namespace. + * + * @param string $className the fully qualified class name without a leading backslash "\" + * @throws UnknownClassException if the class does not exist in the class file + */ + public static function autoload($className) + { + if (isset(self::$classMap[$className])) { + $classFile = self::$classMap[$className]; + if ($classFile[0] === '@') { + $classFile = static::getAlias($classFile); + } + } else { + // follow PSR-0 to determine the class file + if (($pos = strrpos($className, '\\')) !== false) { + // namespaced class, e.g. yii\base\Component + $path = str_replace('\\', '/', substr($className, 0, $pos + 1)) + . str_replace('_', '/', substr($className, $pos + 1)) . '.php'; + } else { + $path = str_replace('_', '/', $className) . '.php'; + } + + // try loading via path alias + if (strpos($path, '/') === false) { + return; + } else { + $classFile = static::getAlias('@' . $path, false); + if ($classFile === false || !is_file($classFile)) { + return; + } + } + } + + include($classFile); + + if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && + (!function_exists('trait_exists') || !trait_exists($className, false))) { + throw new UnknownClassException("Unable to find '$className' in file: $classFile"); + } + } + + /** + * Creates a new object using the given configuration. + * + * The configuration can be either a string or an array. + * If a string, it is treated as the *object class*; if an array, + * it must contain a `class` element specifying the *object class*, and + * the rest of the name-value pairs in the array will be used to initialize + * the corresponding object properties. + * + * The object type can be either a class name or the [[getAlias()|alias]] of + * the class. For example, + * + * - `app\components\GoogleMap`: fully-qualified namespaced class. + * - `@app/components/GoogleMap`: an alias, used for non-namespaced class. + * + * Below are some usage examples: + * + * ~~~ + * $object = \Yii::createObject('@app/components/GoogleMap'); + * $object = \Yii::createObject(array( + * 'class' => '\app\components\GoogleMap', + * 'apiKey' => 'xyz', + * )); + * ~~~ + * + * This method can be used to create any object as long as the object's constructor is + * defined like the following: + * + * ~~~ + * public function __construct(..., $config = array()) { + * } + * ~~~ + * + * The method will pass the given configuration as the last parameter of the constructor, + * and any additional parameters to this method will be passed as the rest of the constructor parameters. + * + * @param string|array $config the configuration. It can be either a string representing the class name + * or an array representing the object configuration. + * @return mixed the created object + * @throws InvalidConfigException if the configuration is invalid. + */ + public static function createObject($config) + { + static $reflections = array(); + + if (is_string($config)) { + $class = $config; + $config = array(); + } elseif (isset($config['class'])) { + $class = $config['class']; + unset($config['class']); + } else { + throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); + } + + $class = ltrim($class, '\\'); + + if (isset(self::$objectConfig[$class])) { + $config = array_merge(self::$objectConfig[$class], $config); + } + + if (($n = func_num_args()) > 1) { + /** @var $reflection \ReflectionClass */ + if (isset($reflections[$class])) { + $reflection = $reflections[$class]; + } else { + $reflection = $reflections[$class] = new \ReflectionClass($class); + } + $args = func_get_args(); + array_shift($args); // remove $config + if (!empty($config)) { + $args[] = $config; + } + return $reflection->newInstanceArgs($args); + } else { + return empty($config) ? new $class : new $class($config); + } + } + + /** + * Logs a trace message. + * Trace messages are logged mainly for development purpose to see + * the execution work flow of some code. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function trace($message, $category = 'application') + { + if (YII_DEBUG) { + self::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category); + } + } + + /** + * Logs an error message. + * An error message is typically logged when an unrecoverable error occurs + * during the execution of an application. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function error($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category); + } + + /** + * Logs a warning message. + * A warning message is typically logged when an error occurs while the execution + * can still continue. + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function warning($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category); + } + + /** + * Logs an informative message. + * An informative message is typically logged by an application to keep record of + * something important (e.g. an administrator logs in). + * @param string $message the message to be logged. + * @param string $category the category of the message. + */ + public static function info($message, $category = 'application') + { + self::$app->getLog()->log($message, Logger::LEVEL_INFO, $category); + } + + /** + * Marks the beginning of a code block for profiling. + * This has to be matched with a call to [[endProfile]] with the same category name. + * The begin- and end- calls must also be properly nested. For example, + * + * ~~~ + * \Yii::beginProfile('block1'); + * // some code to be profiled + * \Yii::beginProfile('block2'); + * // some other code to be profiled + * \Yii::endProfile('block2'); + * \Yii::endProfile('block1'); + * ~~~ + * @param string $token token for the code block + * @param string $category the category of this log message + * @see endProfile + */ + public static function beginProfile($token, $category = 'application') + { + self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); + } + + /** + * Marks the end of a code block for profiling. + * This has to be matched with a previous call to [[beginProfile]] with the same category name. + * @param string $token token for the code block + * @param string $category the category of this log message + * @see beginProfile + */ + public static function endProfile($token, $category = 'application') + { + self::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category); + } + + /** + * Returns an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information. + * @return string an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information + */ + public static function powered() + { + return 'Powered by Yii Framework'; + } + + /** + * Translates a message to the specified language. + * + * This is a shortcut method of [[\yii\i18n\I18N::translate()]]. + * + * The translation will be conducted according to the message category and the target language will be used. + * + * In case when a translated message has different plural forms (separated by "|"), this method + * will also attempt to choose an appropriate one according to a given numeric value which is + * specified as the first parameter (indexed by 0) in `$params`. + * + * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first + * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}" + * will be replaced with the given number. + * + * For more details on how plural rules are applied, please refer to: + * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]] + * + * @param string $category the message category. + * @param string $message the message to be translated. + * @param array $params the parameters that will be used to replace the corresponding placeholders in the message. + * @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current + * [[\yii\base\Application::language|application language]] will be used. + * @return string the translated message. + */ + public static function t($category, $message, $params = array(), $language = null) + { + if (self::$app !== null) { + return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language); + } else { + return is_array($params) ? strtr($message, $params) : $message; + } + } + + /** + * Configures an object with the initial property values. + * @param object $object the object to be configured + * @param array $properties the property initial values given in terms of name-value pairs. + */ + public static function configure($object, $properties) + { + foreach ($properties as $name => $value) { + $object->$name = $value; + } + } + + /** + * Returns the public member variables of an object. + * This method is provided such that we can get the public member variables of an object. + * It is different from "get_object_vars()" because the latter will return private + * and protected variables if it is called within the object itself. + * @param object $object the object to be handled + * @return array the public member variables of the object + */ + public static function getObjectVars($object) + { + return get_object_vars($object); + } +} diff --git a/framework/yii/Yii.php b/framework/yii/Yii.php index 0406463..232117f 100644 --- a/framework/yii/Yii.php +++ b/framework/yii/Yii.php @@ -7,18 +7,18 @@ * @license http://www.yiiframework.com/license/ */ -require(__DIR__ . '/YiiBase.php'); +require(__DIR__ . '/BaseYii.php'); /** * Yii is a helper class serving common framework functionalities. * - * It extends from [[AbstractYii]] which provides the actual implementation. - * By writing your own Yii class, you can customize some functionalities of [[AbstractYii]]. + * It extends from [[BaseYii]] which provides the actual implementation. + * By writing your own Yii class, you can customize some functionalities of [[BaseYii]]. * * @author Qiang Xue * @since 2.0 */ -class Yii extends \yii\AbstractYii +class Yii extends \yii\BaseYii { } diff --git a/framework/yii/classes.php b/framework/yii/classes.php index 22472d7..d4f304c 100644 --- a/framework/yii/classes.php +++ b/framework/yii/classes.php @@ -125,27 +125,27 @@ return array( 'yii\grid\GridViewAsset' => YII_PATH . '/grid/GridViewAsset.php', 'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php', 'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php', - 'yii\helpers\AbstractArrayHelper' => YII_PATH . '/helpers/AbstractArrayHelper.php', + 'yii\helpers\BaseArrayHelper' => YII_PATH . '/helpers/BaseArrayHelper.php', 'yii\helpers\Console' => YII_PATH . '/helpers/Console.php', - 'yii\helpers\AbstractConsole' => YII_PATH . '/helpers/AbstractConsole.php', + 'yii\helpers\BaseConsole' => YII_PATH . '/helpers/BaseConsole.php', 'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php', - 'yii\helpers\AbstractFileHelper' => YII_PATH . '/helpers/AbstractFileHelper.php', + 'yii\helpers\BaseFileHelper' => YII_PATH . '/helpers/BaseFileHelper.php', 'yii\helpers\Html' => YII_PATH . '/helpers/Html.php', - 'yii\helpers\AbstractHtml' => YII_PATH . '/helpers/AbstractHtml.php', + 'yii\helpers\BaseHtml' => YII_PATH . '/helpers/BaseHtml.php', 'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php', - 'yii\helpers\AbstractHtmlPurifier' => YII_PATH . '/helpers/AbstractHtmlPurifier.php', + 'yii\helpers\BaseHtmlPurifier' => YII_PATH . '/helpers/BaseHtmlPurifier.php', 'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php', - 'yii\helpers\AbstractInflector' => YII_PATH . '/helpers/AbstractInflector.php', + 'yii\helpers\BaseInflector' => YII_PATH . '/helpers/BaseInflector.php', 'yii\helpers\Json' => YII_PATH . '/helpers/Json.php', - 'yii\helpers\AbstractJson' => YII_PATH . '/helpers/AbstractJson.php', + 'yii\helpers\BaseJson' => YII_PATH . '/helpers/BaseJson.php', 'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php', - 'yii\helpers\AbstractMarkdown' => YII_PATH . '/helpers/AbstractMarkdown.php', + 'yii\helpers\BaseMarkdown' => YII_PATH . '/helpers/BaseMarkdown.php', 'yii\helpers\Security' => YII_PATH . '/helpers/Security.php', - 'yii\helpers\AbstractSecurity' => YII_PATH . '/helpers/AbstractSecurity.php', + 'yii\helpers\BaseSecurity' => YII_PATH . '/helpers/BaseSecurity.php', 'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php', - 'yii\helpers\AbstractStringHelper' => YII_PATH . '/helpers/AbstractStringHelper.php', + 'yii\helpers\BaseStringHelper' => YII_PATH . '/helpers/BaseStringHelper.php', 'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php', - 'yii\helpers\AbstractVarDumper' => YII_PATH . '/helpers/AbstractVarDumper.php', + 'yii\helpers\BaseVarDumper' => YII_PATH . '/helpers/BaseVarDumper.php', 'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php', 'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php', 'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php', diff --git a/framework/yii/helpers/AbstractArrayHelper.php b/framework/yii/helpers/AbstractArrayHelper.php deleted file mode 100644 index c26c1cd..0000000 --- a/framework/yii/helpers/AbstractArrayHelper.php +++ /dev/null @@ -1,451 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractArrayHelper -{ - /** - * Converts an object or an array of objects into an array. - * @param object|array $object the object to be converted into an array - * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays. - * The properties specified for each class is an array of the following format: - * - * ~~~ - * array( - * 'app\models\Post' => array( - * 'id', - * 'title', - * // the key name in array result => property name - * 'createTime' => 'create_time', - * // the key name in array result => anonymous function - * 'length' => function ($post) { - * return strlen($post->content); - * }, - * ), - * ) - * ~~~ - * - * The result of `ArrayHelper::toArray($post, $properties)` could be like the following: - * - * ~~~ - * array( - * 'id' => 123, - * 'title' => 'test', - * 'createTime' => '2013-01-01 12:00AM', - * 'length' => 301, - * ) - * ~~~ - * - * @param boolean $recursive whether to recursively converts properties which are objects into arrays. - * @return array the array representation of the object - */ - public static function toArray($object, $properties = array(), $recursive = true) - { - if (!empty($properties) && is_object($object)) { - $className = get_class($object); - if (!empty($properties[$className])) { - $result = array(); - foreach ($properties[$className] as $key => $name) { - if (is_int($key)) { - $result[$name] = $object->$name; - } else { - $result[$key] = static::getValue($object, $name); - } - } - return $result; - } - } - if ($object instanceof Arrayable) { - $object = $object->toArray(); - if (!$recursive) { - return $object; - } - } - $result = array(); - foreach ($object as $key => $value) { - if ($recursive && (is_array($value) || is_object($value))) { - $result[$key] = static::toArray($value, true); - } else { - $result[$key] = $value; - } - } - return $result; - } - - /** - * 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 (!empty($args)) { - $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 element if found, default value otherwise - */ - 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; - } - } - - /** - * Removes an item from an array and returns the value. If the key does not exist in the array, the default value - * will be returned instead. - * - * Usage examples, - * - * ~~~ - * // $array = array('type' => 'A', 'options' => array(1, 2)); - * // working with array - * $type = \yii\helpers\ArrayHelper::remove($array, 'type'); - * // $array content - * // $array = array('options' => array(1, 2)); - * ~~~ - * - * @param array $array the array to extract value from - * @param string $key key name of the array element - * @param mixed $default the default value to be returned if the specified key does not exist - * @return mixed|null the value of the element if found, default value otherwise - */ - public static function remove(&$array, $key, $default = null) - { - if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { - $value = $array[$key]; - unset($array[$key]); - return $value; - } - return $default; - } - - /** - * 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 $descending whether to sort in descending or ascending order. When - * sorting by multiple keys with different descending orders, use an array of descending flags. - * @param integer|array $sortFlag the PHP sort flag. Valid values include - * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`. - * Please refer to [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. - * @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter - * is used only when `$sortFlag` is `SORT_STRING`. - * When sorting by multiple keys with different case sensitivities, use an array of boolean values. - * @throws InvalidParamException if the $descending or $sortFlag parameters do not have - * correct number of elements as that of $key. - */ - public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true) - { - $keys = is_array($key) ? $key : array($key); - if (empty($keys) || empty($array)) { - return; - } - $n = count($keys); - if (is_scalar($descending)) { - $descending = array_fill(0, $n, $descending); - } elseif (count($descending) !== $n) { - throw new InvalidParamException('The length of $descending 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 $sortFlag parameter must be the same as that of $keys.'); - } - if (is_scalar($caseSensitive)) { - $caseSensitive = array_fill(0, $n, $caseSensitive); - } elseif (count($caseSensitive) !== $n) { - throw new InvalidParamException('The length of $caseSensitive parameter must be the same as that of $keys.'); - } - $args = array(); - foreach ($keys as $i => $key) { - $flag = $sortFlag[$i]; - $cs = $caseSensitive[$i]; - if (!$cs && ($flag === SORT_STRING)) { - if (defined('SORT_FLAG_CASE')) { - $flag = $flag | SORT_FLAG_CASE; - $args[] = static::getColumn($array, $key); - } else { - $column = array(); - foreach (static::getColumn($array, $key) as $k => $value) { - $column[$k] = mb_strtolower($value); - } - $args[] = $column; - } - } else { - $args[] = static::getColumn($array, $key); - } - $args[] = $descending[$i] ? SORT_DESC : SORT_ASC; - $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; - } -} diff --git a/framework/yii/helpers/AbstractConsole.php b/framework/yii/helpers/AbstractConsole.php deleted file mode 100644 index 8131aae..0000000 --- a/framework/yii/helpers/AbstractConsole.php +++ /dev/null @@ -1,835 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractConsole -{ - 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 RESET = 0; - const NORMAL = 0; - 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"; - } - - /** - * Returns the ANSI format code. - * - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @return string The ANSI format code according to the given formatting constants. - */ - public static function ansiFormatCode($format) - { - return "\033[" . implode(';', $format) . 'm'; - } - - /** - * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards. - * - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @see ansiFormatCode() - * @see ansiFormatEnd() - */ - public static function beginAnsiFormat($format) - { - echo "\033[" . implode(';', $format) . 'm'; - } - - /** - * Resets any ANSI format set by previous method [[ansiFormatBegin()]] - * Any output after this will have default text format. - * This is equal to calling - * - * ```php - * echo Console::ansiFormatCode(array(Console::RESET)) - * ``` - */ - public static function endAnsiFormat() - { - echo "\033[0m"; - } - - /** - * Will return a string formatted with the given ANSI style - * - * @param string $string the string to be formatted - * @param array $format An array containing formatting values. - * You can pass any of the FG_*, BG_* and TEXT_* constants - * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. - * @return string - */ - public static function ansiFormat($string, $format = array()) - { - $code = implode(';', $format); - return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; - } - - /** - * Returns the ansi format code for xterm foreground color. - * You can pass the return value of this to one of the formatting methods: - * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] - * - * @param integer $colorCode xterm color code - * @return string - * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors - */ - public static function xtermFgColor($colorCode) - { - return '38;5;' . $colorCode; - } - - /** - * Returns the ansi format code for xterm background color. - * You can pass the return value of this to one of the formatting methods: - * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] - * - * @param integer $colorCode xterm color code - * @return string - * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors - */ - public static function xtermBgColor($colorCode) - { - return '48;5;' . $colorCode; - } - - /** - * Strips ANSI control codes from a string - * - * @param string $string String to strip - * @return string - */ - public static function stripAnsiFormat($string) - { - return preg_replace('/\033\[[\d;?]*\w/', '', $string); - } - - /** - * Converts an ANSI formatted string to HTML - * @param $string - * @return mixed - */ - // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 - 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 self::FG_BLACK: - $style = array('color' => '#000000'); - break; - case self::FG_BLUE: - $style = array('color' => '#000078'); - break; - case self::FG_CYAN: - $style = array('color' => '#007878'); - break; - case self::FG_GREEN: - $style = array('color' => '#007800'); - break; - case self::FG_GREY: - $style = array('color' => '#787878'); - break; - case self::FG_PURPLE: - $style = array('color' => '#780078'); - break; - case self::FG_RED: - $style = array('color' => '#780000'); - break; - case self::FG_YELLOW: - $style = array('color' => '#787800'); - break; - case self::BG_BLACK: - $style = array('background-color' => '#000000'); - break; - case self::BG_BLUE: - $style = array('background-color' => '#000078'); - break; - case self::BG_CYAN: - $style = array('background-color' => '#007878'); - break; - case self::BG_GREEN: - $style = array('background-color' => '#007800'); - break; - case self::BG_GREY: - $style = array('background-color' => '#787878'); - break; - case self::BG_PURPLE: - $style = array('background-color' => '#780078'); - break; - case self::BG_RED: - $style = array('background-color' => '#780000'); - break; - case self::BG_YELLOW: - $style = array('background-color' => '#787800'); - break; - case self::BOLD: - $style = array('font-weight' => 'bold'); - break; - case self::ITALIC: - $style = array('font-style' => 'italic'); - break; - case self::UNDERLINE: - $style = array('text-decoration' => array('underline')); - break; - case self::OVERLINED: - $style = array('text-decoration' => array('overline')); - break; - case self::CROSSED_OUT: - $style = array('text-decoration' => array('line-through')); - break; - case self::BLINK: - $style = array('text-decoration' => array('blink')); - break; - case self::NEGATIVE: // ??? - case self::CONCEALED: - case self::ENCIRCLED: - case self::FRAMED: - // TODO allow resetting codes - break; - case 0: // ansi reset - $return = ''; - for ($n = $tags; $tags > 0; $tags--) { - $return .= ''; - } - 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 ' $value) { - echo " $key - $value\n"; - } - echo " ? - Show help\n"; - goto top; - } elseif (!in_array($input, array_keys($options))) { - goto top; - } - return $input; - } - - /** - * Displays and updates a simple progress bar on screen. - * - * @param integer $done the number of items that are completed - * @param integer $total the total value of items that are to be done - * @param integer $size the size of the status bar (optional) - * @see http://snipplr.com/view/29548/ - */ - public static function showProgress($done, $total, $size = 30) - { - static $start; - - // if we go over our bound, just ignore it - if ($done > $total) { - return; - } - - if (empty($start)) { - $start = time(); - } - - $now = time(); - - $percent = (double)($done / $total); - $bar = floor($percent * $size); - - $status = "\r["; - $status .= str_repeat("=", $bar); - if ($bar < $size) { - $status .= ">"; - $status .= str_repeat(" ", $size - $bar); - } else { - $status .= "="; - } - - $display = number_format($percent * 100, 0); - - $status .= "] $display% $done/$total"; - - $rate = ($now - $start) / $done; - $left = $total - $done; - $eta = round($rate * $left, 2); - - $elapsed = $now - $start; - - $status .= " remaining: " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec."; - - static::stdout("$status "); - - flush(); - - // when done, send a newline - if ($done == $total) { - echo "\n"; - } - } -} diff --git a/framework/yii/helpers/AbstractFileHelper.php b/framework/yii/helpers/AbstractFileHelper.php deleted file mode 100644 index 5eab927..0000000 --- a/framework/yii/helpers/AbstractFileHelper.php +++ /dev/null @@ -1,329 +0,0 @@ - - * @author Alex Makarov - * @since 2.0 - */ -abstract class AbstractFileHelper -{ - /** - * 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 the 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. - * - * @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); - finfo_close($info); - if ($result !== false) { - return $result; - } - } - } - - return $checkExtension ? static::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) - { - static $mimeTypes = array(); - if ($magicFile === null) { - $magicFile = __DIR__ . '/mimeTypes.php'; - } - if (!isset($mimeTypes[$magicFile])) { - $mimeTypes[$magicFile] = require($magicFile); - } - if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { - $ext = strtolower($ext); - if (isset($mimeTypes[$magicFile][$ext])) { - return $mimeTypes[$magicFile][$ext]; - } - } - return null; - } - - /** - * Copies a whole directory as another one. - * The files and sub-directories will also be copied over. - * @param string $src the source directory - * @param string $dst the destination directory - * @param array $options options for directory copy. Valid options are: - * - * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775. - * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. - * - filter: callback, a PHP callback that is called for each directory or file. - * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. - * The callback can return one of the following values: - * - * * true: the directory or file will be copied (the "only" and "except" options will be ignored) - * * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored) - * * null: the "only" and "except" options will determine whether the directory or file should be copied - * - * - only: array, list of patterns that the file paths should match if they want to be copied. - * A path matches a pattern if it contains the pattern string at its end. - * For example, '.php' matches all file paths ending with '.php'. - * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. - * If a file path matches a pattern in both "only" and "except", it will NOT be copied. - * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied. - * A path matches a pattern if it contains the pattern string at its end. - * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' - * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; - * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches - * both '/' and '\' in the paths. - * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true. - * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied. - * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or - * file copied from, while `$to` is the copy target. - */ - public static function copyDirectory($src, $dst, $options = array()) - { - if (!is_dir($dst)) { - static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true); - } - - $handle = opendir($src); - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $from = $src . DIRECTORY_SEPARATOR . $file; - $to = $dst . DIRECTORY_SEPARATOR . $file; - if (static::filterPath($from, $options)) { - if (is_file($from)) { - copy($from, $to); - if (isset($options['fileMode'])) { - @chmod($to, $options['fileMode']); - } - } else { - static::copyDirectory($from, $to, $options); - } - if (isset($options['afterCopy'])) { - call_user_func($options['afterCopy'], $from, $to); - } - } - } - closedir($handle); - } - - /** - * Removes a directory (and all its content) recursively. - * @param string $dir the directory to be deleted recursively. - */ - public static function removeDirectory($dir) - { - if (!is_dir($dir) || !($handle = opendir($dir))) { - return; - } - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (is_file($path)) { - unlink($path); - } else { - static::removeDirectory($path); - } - } - closedir($handle); - rmdir($dir); - } - - /** - * Returns the files found under the specified directory and subdirectories. - * @param string $dir the directory under which the files will be looked for. - * @param array $options options for file searching. Valid options are: - * - * - filter: callback, a PHP callback that is called for each directory or file. - * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. - * The callback can return one of the following values: - * - * * true: the directory or file will be returned (the "only" and "except" options will be ignored) - * * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored) - * * null: the "only" and "except" options will determine whether the directory or file should be returned - * - * - only: array, list of patterns that the file paths should match if they want to be returned. - * A path matches a pattern if it contains the pattern string at its end. - * For example, '.php' matches all file paths ending with '.php'. - * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. - * If a file path matches a pattern in both "only" and "except", it will NOT be returned. - * - except: array, list of patterns that the file paths or directory paths should match if they want to be excluded from the result. - * A path matches a pattern if it contains the pattern string at its end. - * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' - * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; - * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches - * both '/' and '\' in the paths. - * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. - * @return array files found under the directory. The file list is sorted. - */ - public static function findFiles($dir, $options = array()) - { - $list = array(); - $handle = opendir($dir); - while (($file = readdir($handle)) !== false) { - if ($file === '.' || $file === '..') { - continue; - } - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (static::filterPath($path, $options)) { - if (is_file($path)) { - $list[] = $path; - } elseif (!isset($options['recursive']) || $options['recursive']) { - $list = array_merge($list, static::findFiles($path, $options)); - } - } - } - closedir($handle); - return $list; - } - - /** - * Checks if the given file path satisfies the filtering options. - * @param string $path the path of the file or directory to be checked - * @param array $options the filtering options. See [[findFiles()]] for explanations of - * the supported options. - * @return boolean whether the file or directory satisfies the filtering options. - */ - public static function filterPath($path, $options) - { - if (isset($options['filter'])) { - $result = call_user_func($options['filter'], $path); - if (is_bool($result)) { - return $result; - } - } - $path = str_replace('\\', '/', $path); - if ($isDir = is_dir($path)) { - $path .= '/'; - } - $n = StringHelper::strlen($path); - - if (!empty($options['except'])) { - foreach ($options['except'] as $name) { - if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { - return false; - } - } - } - - if (!$isDir && !empty($options['only'])) { - foreach ($options['only'] as $name) { - if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { - return true; - } - } - return false; - } - return true; - } - - /** - * Creates a new directory. - * - * This method is similar to the PHP `mkdir()` function except that - * it uses `chmod()` to set the permission of the created directory - * in order to avoid the impact of the `umask` setting. - * - * @param string $path path of the directory to be created. - * @param integer $mode the permission to be set for the created directory. - * @param boolean $recursive whether to create parent directories if they do not exist. - * @return boolean whether the directory is created successfully - */ - public static function createDirectory($path, $mode = 0775, $recursive = true) - { - if (is_dir($path)) { - return true; - } - $parentDir = dirname($path); - if ($recursive && !is_dir($parentDir)) { - static::createDirectory($parentDir, $mode, true); - } - $result = mkdir($path, $mode); - chmod($path, $mode); - return $result; - } -} diff --git a/framework/yii/helpers/AbstractHtml.php b/framework/yii/helpers/AbstractHtml.php deleted file mode 100644 index 37e926c..0000000 --- a/framework/yii/helpers/AbstractHtml.php +++ /dev/null @@ -1,1599 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractHtml -{ - /** - * @var array list of void elements (element name => 1) - * @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 array the preferred order of attributes in a tag. This mainly affects the order of the attributes - * that are rendered by [[renderTagAttributes()]]. - */ - 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 - * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false, - * HTML entities in `$content` will not be further encoded. - * @return string the encoded content - * @see decode - * @see http://www.php.net/manual/en/function.htmlspecialchars.php - */ - public static function encode($content, $doubleEncode = true) - { - return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode); - } - - /** - * 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) . '>'; - return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content"; - } - - /** - * 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 ""; - } - - /** - * 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()) - { - return static::tag('style', $content, $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()) - { - return static::tag('script', $content, $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['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['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, such as "post", "get", "put", "delete" (case-insensitive). - * Since most browsers only support "post" and "get", if other methods are given, they will - * be simulated using "post", and a hidden input will be added which contains the actual method type. - * See [[\yii\web\Request::restVar]] for more details. - * @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); - - $hiddenInputs = array(); - - $request = Yii::$app->getRequest(); - if ($request instanceof Request) { - if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) { - // simulate PUT, DELETE, etc. via POST - $hiddenInputs[] = static::hiddenInput($request->restVar, $method); - $method = 'post'; - } - if ($request->enableCsrfValidation) { - $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken()); - } - } - - if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { - // query parameters in the action are ignored for GET method - // we use hidden fields to add them back - foreach (explode('&', substr($action, $pos + 1)) as $pair) { - if (($pos1 = strpos($pair, '=')) !== false) { - $hiddenInputs[] = static::hiddenInput( - urldecode(substr($pair, 0, $pos1)), - urldecode(substr($pair, $pos1 + 1)) - ); - } else { - $hiddenInputs[] = static::hiddenInput(urldecode($pair), ''); - } - } - $action = substr($action, 0, $pos); - } - - $options['action'] = $action; - $options['method'] = $method; - $form = static::beginTag('form', $options); - if (!empty($hiddenInputs)) { - $form .= "\n" . implode("\n", $hiddenInputs); - } - - return $form; - } - - /** - * Generates a form end tag. - * @return string the generated tag - * @see beginForm - */ - public static function endForm() - { - return ''; - } - - /** - * 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 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 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()) - { - $options['href'] = 'mailto:' . ($email === null ? $text : $email); - return static::tag('a', $text, $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', '', $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 [[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 $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 button tag - */ - public static function button($content = 'Button', $options = array()) - { - return static::tag('button', $content, $options); - } - - /** - * Generates a submit button tag. - * @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($content = 'Submit', $options = array()) - { - $options['type'] = 'submit'; - return static::button($content, $options); - } - - /** - * Generates a reset button tag. - * @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($content = 'Reset', $options = array()) - { - $options['type'] = 'reset'; - return static::button($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 === null ? null : (string)$value; - return static::tag('input', '', $options); - } - - /** - * Generates an input button. - * @param string $label 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($label = 'Button', $options = array()) - { - $options['type'] = 'button'; - $options['value'] = $label; - return static::tag('input', '', $options); - } - - /** - * Generates a submit input button. - * @param string $label 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($label = 'Submit', $options = array()) - { - $options['type'] = 'submit'; - $options['value'] = $label; - return static::tag('input', '', $options); - } - - /** - * Generates a reset input button. - * @param string $label 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($label = 'Reset', $options = array()) - { - $options['type'] = 'reset'; - $options['value'] = $label; - return static::tag('input', '', $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 array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - 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. - * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the radio button will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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, $options = array()) - { - $options['checked'] = (boolean)$checked; - $value = array_key_exists('value', $options) ? $options['value'] : '1'; - 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 = ''; - } - if (isset($options['label'])) { - $label = $options['label']; - $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); - unset($options['label'], $options['labelOptions']); - $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions); - return $hidden . static::tag('div', $content, array('class' => 'radio')); - } else { - 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 array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - 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. - * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the checkbox will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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, $options = array()) - { - $options['checked'] = (boolean)$checked; - $value = array_key_exists('value', $options) ? $options['value'] : '1'; - 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 = ''; - } - if (isset($options['label'])) { - $label = $options['label']; - $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); - unset($options['label'], $options['labelOptions']); - $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions); - return $hidden . static::tag('div', $content, array('class' => 'checkbox')); - } else { - 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 specially handled: - * - * - 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 specially handled: - * - * - 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 (!empty($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. - * @param array $options options (name => config) for the checkbox list container tag. - * The following options are specially handled: - * - * - tag: string, the tag name of the container element. - * - 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. - * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. - * This option is ignored if `item` option is set. - * - 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, respectively. - * @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; - $encode = !isset($options['encode']) || $options['encode']; - $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::checkbox($name, $checked, array( - 'value' => $value, - 'label' => $encode ? static::encode($label) : $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"; - - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); - - return $hidden . static::tag($tag, implode($separator, $lines), $options); - } - - /** - * 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. - * @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. - * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. - * This option is ignored if `item` option is set. - * - 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, respectively. - * @return string the generated radio button list - */ - public static function radioList($name, $selection = null, $items = array(), $options = array()) - { - $encode = !isset($options['encode']) || $options['encode']; - $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::radio($name, $checked, array( - 'value' => $value, - 'label' => $encode ? static::encode($label) : $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 = ''; - } - - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); - - return $hidden . static::tag($tag, implode($separator, $lines), $options); - } - - /** - * Generates an unordered list. - * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. - * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. - * @param array $options options (name => config) for the radio button list. The following options are supported: - * - * - encode: boolean, whether to HTML-encode the items. Defaults to true. - * This option is ignored if the `item` option is specified. - * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. - * - item: callable, a callback that is used to generate each individual list item. - * The signature of this callback must be: - * - * ~~~ - * function ($item, $index) - * ~~~ - * - * where $index is the array key corresponding to `$item` in `$items`. The callback should return - * the whole list item tag. - * - * @return string the generated unordered list. An empty string is returned if `$items` is empty. - */ - public static function ul($items, $options = array()) - { - if (empty($items)) { - return ''; - } - $tag = isset($options['tag']) ? $options['tag'] : 'ul'; - $encode = !isset($options['encode']) || $options['encode']; - $formatter = isset($options['item']) ? $options['item'] : null; - $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : array(); - unset($options['tag'], $options['encode'], $options['item'], $options['itemOptions']); - $results = array(); - foreach ($items as $index => $item) { - if ($formatter !== null) { - $results[] = call_user_func($formatter, $item, $index); - } else { - $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions); - } - } - return static::tag($tag, "\n" . implode("\n", $results) . "\n", $options); - } - - /** - * Generates an ordered list. - * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. - * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. - * @param array $options options (name => config) for the radio button list. The following options are supported: - * - * - encode: boolean, whether to HTML-encode the items. Defaults to true. - * This option is ignored if the `item` option is specified. - * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. - * - item: callable, a callback that is used to generate each individual list item. - * The signature of this callback must be: - * - * ~~~ - * function ($item, $index) - * ~~~ - * - * where $index is the array key corresponding to `$item` in `$items`. The callback should return - * the whole list item tag. - * - * @return string the generated ordered list. An empty string is returned if `$items` is empty. - */ - public static function ol($items, $options = array()) - { - $options['tag'] = 'ol'; - return static::ul($items, $options); - } - - /** - * Generates a label tag for the given model attribute. - * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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. - * The following options are specially handled: - * - * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]]. - * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display - * (after encoding). - * - * @return string the generated label tag - */ - public static function activeLabel($model, $attribute, $options = array()) - { - $attribute = static::getAttributeName($attribute); - $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute)); - $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute); - unset($options['label'], $options['for']); - return static::label($label, $for, $options); - } - - /** - * Generates a tag that contains the first validation error of the specified model attribute. - * Note that even if there is no validation error, this method will still return an empty error tag. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded - * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. - * - * The following options are specially handled: - * - * - tag: this specifies the tag name. If not set, "div" will be used. - * - * @return string the generated label tag - */ - public static function error($model, $attribute, $options = array()) - { - $attribute = static::getAttributeName($attribute); - $error = $model->getFirstError($attribute); - $tag = isset($options['tag']) ? $options['tag'] : 'div'; - unset($options['tag']); - return Html::tag($tag, Html::encode($error), $options); - } - - /** - * Generates an input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param string $type the input type (e.g. 'text', 'password') - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeInput($type, $model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::input($type, $name, $value, $options); - } - - /** - * Generates a text input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeTextInput($model, $attribute, $options = array()) - { - return static::activeInput('text', $model, $attribute, $options); - } - - /** - * Generates a hidden input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeHiddenInput($model, $attribute, $options = array()) - { - return static::activeInput('hidden', $model, $attribute, $options); - } - - /** - * Generates a password input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activePasswordInput($model, $attribute, $options = array()) - { - return static::activeInput('password', $model, $attribute, $options); - } - - /** - * Generates a file input tag for the given model attribute. - * This method will generate the "name" and "value" tag attributes automatically for the model attribute - * unless they are explicitly specified in `$options`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated input tag - */ - public static function activeFileInput($model, $attribute, $options = array()) - { - // add a hidden field so that if a model only has a file field, we can - // still use isset($_POST[$modelClass]) to detect if the input is submitted - return static::activeHiddenInput($model, $attribute, array('id' => null, 'value' => '')) - . static::activeInput('file', $model, $attribute, $options); - } - - /** - * Generates a textarea tag for the given model attribute. - * The model attribute value will be used as the content in the textarea. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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()]]. - * @return string the generated textarea tag - */ - public static function activeTextarea($model, $attribute, $options = array()) - { - $name = static::getInputName($model, $attribute); - $value = static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::textarea($name, $value, $options); - } - - /** - * Generates a radio button tag for the given model attribute. - * This method will generate the "checked" tag attribute according to the model attribute value. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, - * it will take the default value '0'. This method will render a hidden input 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. - * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the radio button will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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 activeRadio($model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('uncheck', $options)) { - $options['uncheck'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::radio($name, $checked, $options); - } - - /** - * Generates a checkbox tag for the given model attribute. - * This method will generate the "checked" tag attribute according to the model attribute value. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, - * it will take the default value '0'. This method will render a hidden input 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. - * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. - * When this option is specified, the checkbox will be enclosed by a label tag. - * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. - * - * 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 activeCheckbox($model, $attribute, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('uncheck', $options)) { - $options['uncheck'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::checkbox($name, $checked, $options); - } - - /** - * Generates a drop-down list for the given model attribute. - * The selection of the drop-down list is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeDropDownList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::dropDownList($name, $checked, $items, $options); - } - - /** - * Generates a list box. - * The selection of the list box is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeListBox($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::listBox($name, $checked, $items, $options); - } - - /** - * Generates a list of checkboxes. - * A checkbox list allows multiple selection, like [[listBox()]]. - * As a result, the corresponding submitted value is an array. - * The selection of the checkbox list is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeCheckboxList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::checkboxList($name, $checked, $items, $options); - } - - /** - * Generates a list of radio buttons. - * A radio button list is like a checkbox list, except that it only allows single selection. - * The selection of the radio buttons is taken from the value of the model attribute. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format - * about attribute expression. - * @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 specially handled: - * - * - 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 activeRadioList($model, $attribute, $items, $options = array()) - { - $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); - $checked = static::getAttributeValue($model, $attribute); - if (!array_key_exists('unselect', $options)) { - $options['unselect'] = '0'; - } - if (!array_key_exists('id', $options)) { - $options['id'] = static::getInputId($model, $attribute); - } - return static::radioList($name, $checked, $items, $options); - } - - /** - * 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'] = (string)$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. - * Attributes whose values are of boolean type will be treated as - * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes). - * And attributes whose values are null will not be rendered. - * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. - * @return string the rendering result. If the attributes are not empty, they will be rendered - * into a string with a leading white space (so 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 (is_bool($value)) { - if ($value) { - $html .= " $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 first be processed by [[Yii::getAlias()]]. If the result - * is an absolute URL, it will be returned without any change further; Otherwise, the result - * will be prefixed with [[\yii\web\Request::baseUrl]] 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')`. - * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used. - * - * @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 instanceof \yii\web\Controller) { - 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 { - $url = Yii::getAlias($url); - if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) { - return $url; - } else { - return Yii::$app->getRequest()->getBaseUrl() . '/' . $url; - } - } - } - - /** - * Adds a CSS class to the specified options. - * If the CSS class is already in the options, it will not be added again. - * @param array $options the options to be modified. - * @param string $class the CSS class to be added - */ - public static function addCssClass(&$options, $class) - { - if (isset($options['class'])) { - $classes = ' ' . $options['class'] . ' '; - if (($pos = strpos($classes, ' ' . $class . ' ')) === false) { - $options['class'] .= ' ' . $class; - } - } else { - $options['class'] = $class; - } - } - - /** - * Removes a CSS class from the specified options. - * @param array $options the options to be modified. - * @param string $class the CSS class to be removed - */ - public static function removeCssClass(&$options, $class) - { - if (isset($options['class'])) { - $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY)); - if (($index = array_search($class, $classes)) !== false) { - unset($classes[$index]); - } - if (empty($classes)) { - unset($options['class']); - } else { - $options['class'] = implode(' ', $classes); - } - } - } - - /** - * Returns the real attribute name from the given attribute expression. - * - * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. - * It is mainly used in tabular data input and/or input of array type. Below are some examples: - * - * - `[0]content` is used in tabular data input to represent the "content" attribute - * for the first model in tabular input; - * - `dates[0]` represents the first array element of the "dates" attribute; - * - `[0]dates[0]` represents the first array element of the "dates" attribute - * for the first model in tabular input. - * - * If `$attribute` has neither prefix nor suffix, it will be returned back without change. - * @param string $attribute the attribute name or expression - * @return string the attribute name without prefix and suffix. - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getAttributeName($attribute) - { - if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - return $matches[2]; - } else { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - } - - /** - * Returns the value of the specified attribute name or expression. - * - * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`. - * See [[getAttributeName()]] for more details about attribute expression. - * - * @param Model $model the model object - * @param string $attribute the attribute name or expression - * @return mixed the corresponding attribute value - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getAttributeValue($model, $attribute) - { - if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - $attribute = $matches[2]; - $index = $matches[3]; - if ($index === '') { - return $model->$attribute; - } else { - $value = $model->$attribute; - foreach (explode('][', trim($index, '[]')) as $id) { - if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) { - $value = $value[$id]; - } else { - return null; - } - } - return $value; - } - } - - /** - * Generates an appropriate input name for the specified attribute name or expression. - * - * This method generates a name that can be used as the input name to collect user input - * for the specified attribute. The name is generated according to the [[Model::formName|form name]] - * of the model and the given attribute name. For example, if the form name of the `Post` model - * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`. - * - * See [[getAttributeName()]] for explanation of attribute expression. - * - * @param Model $model the model object - * @param string $attribute the attribute name or expression - * @return string the generated input name - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getInputName($model, $attribute) - { - $formName = $model->formName(); - if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { - throw new InvalidParamException('Attribute name must contain word characters only.'); - } - $prefix = $matches[1]; - $attribute = $matches[2]; - $suffix = $matches[3]; - if ($formName === '' && $prefix === '') { - return $attribute . $suffix; - } elseif ($formName !== '') { - return $formName . $prefix . "[$attribute]" . $suffix; - } else { - throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.'); - } - } - - /** - * Generates an appropriate input ID for the specified attribute name or expression. - * - * This method converts the result [[getInputName()]] into a valid input ID. - * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`. - * @param Model $model the model object - * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression. - * @return string the generated input ID - * @throws InvalidParamException if the attribute name contains non-word characters. - */ - public static function getInputId($model, $attribute) - { - $name = strtolower(static::getInputName($model, $attribute)); - return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name); - } -} diff --git a/framework/yii/helpers/AbstractHtmlPurifier.php b/framework/yii/helpers/AbstractHtmlPurifier.php deleted file mode 100644 index 221fc37..0000000 --- a/framework/yii/helpers/AbstractHtmlPurifier.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractHtmlPurifier -{ - /** - * Passes markup through HTMLPurifier making it safe to output to end user - * - * @param string $content - * @param array|null $config - * @return string - */ - public static function process($content, $config = null) - { - $configInstance = \HTMLPurifier_Config::create($config); - $configInstance->autoFinalize = false; - $purifier=\HTMLPurifier::instance($configInstance); - $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath()); - return $purifier->purify($content); - } -} diff --git a/framework/yii/helpers/AbstractInflector.php b/framework/yii/helpers/AbstractInflector.php deleted file mode 100644 index 27ee4f7..0000000 --- a/framework/yii/helpers/AbstractInflector.php +++ /dev/null @@ -1,480 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractInflector -{ - /** - * @var array the rules for converting a word into its plural form. - * The keys are the regular expressions and the values are the corresponding replacements. - */ - public static $plurals = array( - '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1', - '/^(sea[- ]bass)$/i' => '\1', - '/(m)ove$/i' => '\1oves', - '/(f)oot$/i' => '\1eet', - '/(h)uman$/i' => '\1umans', - '/(s)tatus$/i' => '\1tatuses', - '/(s)taff$/i' => '\1taff', - '/(t)ooth$/i' => '\1eeth', - '/(quiz)$/i' => '\1zes', - '/^(ox)$/i' => '\1\2en', - '/([m|l])ouse$/i' => '\1ice', - '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', - '/(x|ch|ss|sh)$/i' => '\1es', - '/([^aeiouy]|qu)y$/i' => '\1ies', - '/(hive)$/i' => '\1s', - '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', - '/sis$/i' => 'ses', - '/([ti])um$/i' => '\1a', - '/(p)erson$/i' => '\1eople', - '/(m)an$/i' => '\1en', - '/(c)hild$/i' => '\1hildren', - '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes', - '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', - '/us$/i' => 'uses', - '/(alias)$/i' => '\1es', - '/(ax|cris|test)is$/i' => '\1es', - '/s$/' => 's', - '/^$/' => '', - '/$/' => 's', - ); - /** - * @var array the rules for converting a word into its singular form. - * The keys are the regular expressions and the values are the corresponding replacements. - */ - public static $singulars = array( - '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1', - '/^(sea[- ]bass)$/i' => '\1', - '/(s)tatuses$/i' => '\1tatus', - '/(f)eet$/i' => '\1oot', - '/(t)eeth$/i' => '\1ooth', - '/^(.*)(menu)s$/i' => '\1\2', - '/(quiz)zes$/i' => '\\1', - '/(matr)ices$/i' => '\1ix', - '/(vert|ind)ices$/i' => '\1ex', - '/^(ox)en/i' => '\1', - '/(alias)(es)*$/i' => '\1', - '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', - '/([ftw]ax)es/i' => '\1', - '/(cris|ax|test)es$/i' => '\1is', - '/(shoe|slave)s$/i' => '\1', - '/(o)es$/i' => '\1', - '/ouses$/' => 'ouse', - '/([^a])uses$/' => '\1us', - '/([m|l])ice$/i' => '\1ouse', - '/(x|ch|ss|sh)es$/i' => '\1', - '/(m)ovies$/i' => '\1\2ovie', - '/(s)eries$/i' => '\1\2eries', - '/([^aeiouy]|qu)ies$/i' => '\1y', - '/([lr])ves$/i' => '\1f', - '/(tive)s$/i' => '\1', - '/(hive)s$/i' => '\1', - '/(drive)s$/i' => '\1', - '/([^fo])ves$/i' => '\1fe', - '/(^analy)ses$/i' => '\1sis', - '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', - '/([ti])a$/i' => '\1um', - '/(p)eople$/i' => '\1\2erson', - '/(m)en$/i' => '\1an', - '/(c)hildren$/i' => '\1\2hild', - '/(n)ews$/i' => '\1\2ews', - '/eaus$/' => 'eau', - '/^(.*us)$/' => '\\1', - '/s$/i' => '', - ); - /** - * @var array the special rules for converting a word between its plural form and singular form. - * The keys are the special words in singular form, and the values are the corresponding plural form. - */ - public static $specials = array( - 'atlas' => 'atlases', - 'beef' => 'beefs', - 'brother' => 'brothers', - 'cafe' => 'cafes', - 'child' => 'children', - 'cookie' => 'cookies', - 'corpus' => 'corpuses', - 'cow' => 'cows', - 'curve' => 'curves', - 'foe' => 'foes', - 'ganglion' => 'ganglions', - 'genie' => 'genies', - 'genus' => 'genera', - 'graffito' => 'graffiti', - 'hoof' => 'hoofs', - 'loaf' => 'loaves', - 'man' => 'men', - 'money' => 'monies', - 'mongoose' => 'mongooses', - 'move' => 'moves', - 'mythos' => 'mythoi', - 'niche' => 'niches', - 'numen' => 'numina', - 'occiput' => 'occiputs', - 'octopus' => 'octopuses', - 'opus' => 'opuses', - 'ox' => 'oxen', - 'penis' => 'penises', - 'sex' => 'sexes', - 'soliloquy' => 'soliloquies', - 'testis' => 'testes', - 'trilby' => 'trilbys', - 'turf' => 'turfs', - 'wave' => 'waves', - 'Amoyese' => 'Amoyese', - 'bison' => 'bison', - 'Borghese' => 'Borghese', - 'bream' => 'bream', - 'breeches' => 'breeches', - 'britches' => 'britches', - 'buffalo' => 'buffalo', - 'cantus' => 'cantus', - 'carp' => 'carp', - 'chassis' => 'chassis', - 'clippers' => 'clippers', - 'cod' => 'cod', - 'coitus' => 'coitus', - 'Congoese' => 'Congoese', - 'contretemps' => 'contretemps', - 'corps' => 'corps', - 'debris' => 'debris', - 'diabetes' => 'diabetes', - 'djinn' => 'djinn', - 'eland' => 'eland', - 'elk' => 'elk', - 'equipment' => 'equipment', - 'Faroese' => 'Faroese', - 'flounder' => 'flounder', - 'Foochowese' => 'Foochowese', - 'gallows' => 'gallows', - 'Genevese' => 'Genevese', - 'Genoese' => 'Genoese', - 'Gilbertese' => 'Gilbertese', - 'graffiti' => 'graffiti', - 'headquarters' => 'headquarters', - 'herpes' => 'herpes', - 'hijinks' => 'hijinks', - 'Hottentotese' => 'Hottentotese', - 'information' => 'information', - 'innings' => 'innings', - 'jackanapes' => 'jackanapes', - 'Kiplingese' => 'Kiplingese', - 'Kongoese' => 'Kongoese', - 'Lucchese' => 'Lucchese', - 'mackerel' => 'mackerel', - 'Maltese' => 'Maltese', - 'mews' => 'mews', - 'moose' => 'moose', - 'mumps' => 'mumps', - 'Nankingese' => 'Nankingese', - 'news' => 'news', - 'nexus' => 'nexus', - 'Niasese' => 'Niasese', - 'Pekingese' => 'Pekingese', - 'Piedmontese' => 'Piedmontese', - 'pincers' => 'pincers', - 'Pistoiese' => 'Pistoiese', - 'pliers' => 'pliers', - 'Portuguese' => 'Portuguese', - 'proceedings' => 'proceedings', - 'rabies' => 'rabies', - 'rice' => 'rice', - 'rhinoceros' => 'rhinoceros', - 'salmon' => 'salmon', - 'Sarawakese' => 'Sarawakese', - 'scissors' => 'scissors', - 'series' => 'series', - 'Shavese' => 'Shavese', - 'shears' => 'shears', - 'siemens' => 'siemens', - 'species' => 'species', - 'swine' => 'swine', - 'testes' => 'testes', - 'trousers' => 'trousers', - 'trout' => 'trout', - 'tuna' => 'tuna', - 'Vermontese' => 'Vermontese', - 'Wenchowese' => 'Wenchowese', - 'whiting' => 'whiting', - 'wildebeest' => 'wildebeest', - 'Yengeese' => 'Yengeese', - ); - /** - * @var array map of special chars and its translation. This is used by [[slug()]]. - */ - public static $transliteration = array( - '/ä|æ|ǽ/' => 'ae', - '/ö|œ/' => 'oe', - '/ü/' => 'ue', - '/Ä/' => 'Ae', - '/Ü/' => 'Ue', - '/Ö/' => 'Oe', - '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', - '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', - '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', - '/ç|ć|ĉ|ċ|č/' => 'c', - '/Ð|Ď|Đ/' => 'D', - '/ð|ď|đ/' => 'd', - '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E', - '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e', - '/Ĝ|Ğ|Ġ|Ģ/' => 'G', - '/ĝ|ğ|ġ|ģ/' => 'g', - '/Ĥ|Ħ/' => 'H', - '/ĥ|ħ/' => 'h', - '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I', - '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i', - '/Ĵ/' => 'J', - '/ĵ/' => 'j', - '/Ķ/' => 'K', - '/ķ/' => 'k', - '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L', - '/ĺ|ļ|ľ|ŀ|ł/' => 'l', - '/Ñ|Ń|Ņ|Ň/' => 'N', - '/ñ|ń|ņ|ň|ʼn/' => 'n', - '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O', - '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o', - '/Ŕ|Ŗ|Ř/' => 'R', - '/ŕ|ŗ|ř/' => 'r', - '/Ś|Ŝ|Ş|Ș|Š/' => 'S', - '/ś|ŝ|ş|ș|š|ſ/' => 's', - '/Ţ|Ț|Ť|Ŧ/' => 'T', - '/ţ|ț|ť|ŧ/' => 't', - '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U', - '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u', - '/Ý|Ÿ|Ŷ/' => 'Y', - '/ý|ÿ|ŷ/' => 'y', - '/Ŵ/' => 'W', - '/ŵ/' => 'w', - '/Ź|Ż|Ž/' => 'Z', - '/ź|ż|ž/' => 'z', - '/Æ|Ǽ/' => 'AE', - '/ß/' => 'ss', - '/IJ/' => 'IJ', - '/ij/' => 'ij', - '/Œ/' => 'OE', - '/ƒ/' => 'f' - ); - - /** - * 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 $word the word to be pluralized - * @return string the pluralized word - */ - public static function pluralize($word) - { - if (isset(self::$specials[$word])) { - return self::$specials[$word]; - } - foreach (static::$plurals as $rule => $replacement) { - if (preg_match($rule, $word)) { - return preg_replace($rule, $replacement, $word); - } - } - return $word; - } - - /** - * Returns the singular of the $word - * @param string $word the english word to singularize - * @return string Singular noun. - */ - public static function singularize($word) - { - $result = array_search($word, self::$specials, true); - if ($result !== false) { - return $result; - } - foreach (static::$singulars as $rule => $replacement) { - if (preg_match($rule, $word)) { - return preg_replace($rule, $replacement, $word); - } - } - return $word; - } - - /** - * Converts an underscored or CamelCase word into a English - * sentence. - * @param string $words - * @param bool $ucAll whether to set all words to uppercase - * @return string - */ - public static function titleize($words, $ucAll = false) - { - $words = static::humanize(static::underscore($words), $ucAll); - return $ucAll ? ucwords($words) : ucfirst($words); - } - - /** - * Returns given word as CamelCased - * Converts a word like "send_email" to "SendEmail". It - * will remove non alphanumeric character from the word, so - * "who's online" will be converted to "WhoSOnline" - * @see variablize - * @param string $word the word to CamelCase - * @return string - */ - public static function camelize($word) - { - return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); - } - - /** - * 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('/(? ' ', - '/\\s+/' => $replacement, - '/(?<=[a-z])([A-Z])/' => $replacement . '\\1', - str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => '' - ); - return preg_replace(array_keys($map), array_values($map), $string); - } - - /** - * Converts a table name to its class name. For example, converts "people" to "Person" - * @param string $tableName - * @return string - */ - public static function classify($tableName) - { - return static::camelize(static::singularize($tableName)); - } - - /** - * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ... - * @param int $number the number to get its ordinal value - * @return string - */ - public static function ordinalize($number) - { - if (in_array(($number % 100), range(11, 13))) { - return $number . 'th'; - } - switch ($number % 10) { - case 1: return $number . 'st'; - case 2: return $number . 'nd'; - case 3: return $number . 'rd'; - default: return $number . 'th'; - } - } -} diff --git a/framework/yii/helpers/AbstractJson.php b/framework/yii/helpers/AbstractJson.php deleted file mode 100644 index cda71a0..0000000 --- a/framework/yii/helpers/AbstractJson.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractJson -{ - /** - * Encodes the given value into a JSON string. - * The method enhances `json_encode()` by supporting JavaScript expressions. - * In particular, the method will not encode a JavaScript expression that is - * represented in terms of a [[JsExpression]] object. - * @param mixed $value the data to be encoded - * @param integer $options the encoding options. For more details please refer to - * [[http://www.php.net/manual/en/function.json-encode.php]] - * @return string the encoding result - */ - public static function encode($value, $options = 0) - { - $expressions = array(); - $value = static::processData($value, $expressions, uniqid()); - $json = json_encode($value, $options); - return empty($expressions) ? $json : strtr($json, $expressions); - } - - /** - * Decodes the given JSON string into a PHP data structure. - * @param string $json the JSON string to be decoded - * @param boolean $asArray whether to return objects in terms of associative arrays. - * @return mixed the PHP data - * @throws InvalidParamException if there is any decoding error - */ - public static function decode($json, $asArray = true) - { - if (is_array($json)) { - throw new InvalidParamException('Invalid JSON data.'); - } - $decode = json_decode((string)$json, $asArray); - switch (json_last_error()) { - case JSON_ERROR_NONE: - break; - case JSON_ERROR_DEPTH: - throw new InvalidParamException('The maximum stack depth has been exceeded.'); - case JSON_ERROR_CTRL_CHAR: - throw new InvalidParamException('Control character error, possibly incorrectly encoded.'); - case JSON_ERROR_SYNTAX: - throw new InvalidParamException('Syntax error.'); - case JSON_ERROR_STATE_MISMATCH: - throw new InvalidParamException('Invalid or malformed JSON.'); - case JSON_ERROR_UTF8: - throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.'); - default: - throw new InvalidParamException('Unknown JSON decoding error.'); - } - - return $decode; - } - - /** - * Pre-processes the data before sending it to `json_encode()`. - * @param mixed $data the data to be processed - * @param array $expressions collection of JavaScript expressions - * @param string $expPrefix a prefix internally used to handle JS expressions - * @return mixed the processed data - */ - protected static function processData($data, &$expressions, $expPrefix) - { - if (is_array($data)) { - foreach ($data as $key => $value) { - if (is_array($value) || is_object($value)) { - $data[$key] = static::processData($value, $expressions, $expPrefix); - } - } - return $data; - } elseif (is_object($data)) { - if ($data instanceof JsExpression) { - $token = "!{[$expPrefix=" . count($expressions) . ']}!'; - $expressions['"' . $token . '"'] = $data->expression; - return $token; - } else { - $data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data); - $result = array(); - foreach ($data as $key => $value) { - if (is_array($value) || is_object($value)) { - $result[$key] = static::processData($value, $expressions, $expPrefix); - } else { - $result[$key] = $value; - } - } - return $result; - } - } else { - return $data; - } - } -} diff --git a/framework/yii/helpers/AbstractMarkdown.php b/framework/yii/helpers/AbstractMarkdown.php deleted file mode 100644 index 6b76b44..0000000 --- a/framework/yii/helpers/AbstractMarkdown.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @since 2.0 - */ -abstract class AbstractMarkdown -{ - /** - * @var MarkdownExtra - */ - protected static $markdown; - - /** - * Converts markdown into HTML - * - * @param string $content - * @param array $config - * @return string - */ - public static function process($content, $config = array()) - { - if (static::$markdown === null) { - static::$markdown = new MarkdownExtra(); - } - foreach ($config as $name => $value) { - static::$markdown->{$name} = $value; - } - return static::$markdown->transform($content); - } -} diff --git a/framework/yii/helpers/AbstractSecurity.php b/framework/yii/helpers/AbstractSecurity.php deleted file mode 100644 index d308b7f..0000000 --- a/framework/yii/helpers/AbstractSecurity.php +++ /dev/null @@ -1,285 +0,0 @@ - - * @author Tom Worster - * @since 2.0 - */ -abstract class AbstractSecurity -{ - /** - * 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(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); - 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(); - // 192-bit (24 bytes) key size - $key = StringHelper::substr($key, 0, 24); - $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 = array(); - if (is_file($keyFile)) { - $keys = require($keyFile); - } - } - if (!isset($keys[$name])) { - $keys[$name] = static::generateRandomKey($length); - file_put_contents($keyFile, " 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 > 31) { - 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; - } -} diff --git a/framework/yii/helpers/AbstractStringHelper.php b/framework/yii/helpers/AbstractStringHelper.php deleted file mode 100644 index 050481e..0000000 --- a/framework/yii/helpers/AbstractStringHelper.php +++ /dev/null @@ -1,138 +0,0 @@ - - * @author Alex Makarov - * @since 2.0 - */ -abstract class AbstractStringHelper -{ - /** - * Returns the number of bytes in the given string. - * This method ensures the string is treated as a byte array by using `mb_strlen()`. - * @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 mb_strlen($string, '8bit'); - } - - /** - * Returns the portion of string specified by the start and length parameters. - * This method ensures the string is treated as a byte array by using `mb_substr()`. - * @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 mb_substr($string, $start, $length, '8bit'); - } - - /** - * Returns the trailing name component of a path. - * This method is similar to the php function `basename()` except that it will - * treat both \ and / as directory separators, independent of the operating system. - * This method was mainly created to work on php namespaces. When working with real - * file paths, php's `basename()` should work fine for you. - * Note: this method is not aware of the actual filesystem, or path components such as "..". - * @param string $path A path string. - * @param string $suffix If the name component ends in suffix this will also be cut off. - * @return string the trailing name component of the given path. - * @see http://www.php.net/manual/en/function.basename.php - */ - public static function basename($path, $suffix = '') - { - if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) { - $path = mb_substr($path, 0, -$len); - } - $path = rtrim(str_replace('\\', '/', $path), '/\\'); - if (($pos = mb_strrpos($path, '/')) !== false) { - return mb_substr($path, $pos + 1); - } - return $path; - } - - /** - * Returns parent directory's path. - * This method is similar to `dirname()` except that it will treat - * both \ and / as directory separators, independent of the operating system. - * @param string $path A path string. - * @return string the parent directory's path. - * @see http://www.php.net/manual/en/function.basename.php - */ - public static function dirname($path) - { - $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); - if ($pos !== false) { - return mb_substr($path, 0, $pos); - } else { - return $path; - } - } - - /** - * Compares two strings or string arrays, and return their differences. - * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package. - * @param string|array $lines1 the first string or string array to be compared. If it is a string, - * it will be converted into a string array by breaking at newlines. - * @param string|array $lines2 the second string or string array to be compared. If it is a string, - * it will be converted into a string array by breaking at newlines. - * @param string $format the output format. It must be 'inline', 'unified', 'context', 'side-by-side', or 'array'. - * @return string|array the comparison result. An array is returned if `$format` is 'array'. For all other - * formats, a string is returned. - * @throws InvalidParamException if the format is invalid. - */ - public static function diff($lines1, $lines2, $format = 'inline') - { - if (!is_array($lines1)) { - $lines1 = explode("\n", $lines1); - } - if (!is_array($lines2)) { - $lines2 = explode("\n", $lines2); - } - foreach ($lines1 as $i => $line) { - $lines1[$i] = rtrim($line, "\r\n"); - } - foreach ($lines2 as $i => $line) { - $lines2[$i] = rtrim($line, "\r\n"); - } - switch ($format) { - case 'inline': - $renderer = new \Diff_Renderer_Html_Inline(); - break; - case 'array': - $renderer = new \Diff_Renderer_Html_Array(); - break; - case 'side-by-side': - $renderer = new \Diff_Renderer_Html_SideBySide(); - break; - case 'context': - $renderer = new \Diff_Renderer_Text_Context(); - break; - case 'unified': - $renderer = new \Diff_Renderer_Text_Unified(); - break; - default: - throw new InvalidParamException("Output format must be 'inline', 'side-by-side', 'array', 'context' or 'unified'."); - } - $diff = new \Diff($lines1, $lines2); - return $diff->render($renderer); - } -} diff --git a/framework/yii/helpers/AbstractVarDumper.php b/framework/yii/helpers/AbstractVarDumper.php deleted file mode 100644 index 2c9f194..0000000 --- a/framework/yii/helpers/AbstractVarDumper.php +++ /dev/null @@ -1,127 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2011 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\helpers; - -/** - * AbstractVarDumper provides concrete implementation for [[VarDumper]]. - * - * Do not use AbstractVarDumper. Use [[VarDumper]] instead. - * - * @author Qiang Xue - * @since 2.0 - */ -abstract class AbstractVarDumper -{ - 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 static::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("/', '', $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 = array_push(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; - } - } -} diff --git a/framework/yii/helpers/ArrayHelper.php b/framework/yii/helpers/ArrayHelper.php index a63d3c2..9d428f5 100644 --- a/framework/yii/helpers/ArrayHelper.php +++ b/framework/yii/helpers/ArrayHelper.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class ArrayHelper extends AbstractArrayHelper +class ArrayHelper extends BaseArrayHelper { } diff --git a/framework/yii/helpers/BaseArrayHelper.php b/framework/yii/helpers/BaseArrayHelper.php new file mode 100644 index 0000000..0ed584f --- /dev/null +++ b/framework/yii/helpers/BaseArrayHelper.php @@ -0,0 +1,451 @@ + + * @since 2.0 + */ +class BaseArrayHelper +{ + /** + * Converts an object or an array of objects into an array. + * @param object|array $object the object to be converted into an array + * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays. + * The properties specified for each class is an array of the following format: + * + * ~~~ + * array( + * 'app\models\Post' => array( + * 'id', + * 'title', + * // the key name in array result => property name + * 'createTime' => 'create_time', + * // the key name in array result => anonymous function + * 'length' => function ($post) { + * return strlen($post->content); + * }, + * ), + * ) + * ~~~ + * + * The result of `ArrayHelper::toArray($post, $properties)` could be like the following: + * + * ~~~ + * array( + * 'id' => 123, + * 'title' => 'test', + * 'createTime' => '2013-01-01 12:00AM', + * 'length' => 301, + * ) + * ~~~ + * + * @param boolean $recursive whether to recursively converts properties which are objects into arrays. + * @return array the array representation of the object + */ + public static function toArray($object, $properties = array(), $recursive = true) + { + if (!empty($properties) && is_object($object)) { + $className = get_class($object); + if (!empty($properties[$className])) { + $result = array(); + foreach ($properties[$className] as $key => $name) { + if (is_int($key)) { + $result[$name] = $object->$name; + } else { + $result[$key] = static::getValue($object, $name); + } + } + return $result; + } + } + if ($object instanceof Arrayable) { + $object = $object->toArray(); + if (!$recursive) { + return $object; + } + } + $result = array(); + foreach ($object as $key => $value) { + if ($recursive && (is_array($value) || is_object($value))) { + $result[$key] = static::toArray($value, true); + } else { + $result[$key] = $value; + } + } + return $result; + } + + /** + * 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 (!empty($args)) { + $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 element if found, default value otherwise + */ + 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; + } + } + + /** + * Removes an item from an array and returns the value. If the key does not exist in the array, the default value + * will be returned instead. + * + * Usage examples, + * + * ~~~ + * // $array = array('type' => 'A', 'options' => array(1, 2)); + * // working with array + * $type = \yii\helpers\ArrayHelper::remove($array, 'type'); + * // $array content + * // $array = array('options' => array(1, 2)); + * ~~~ + * + * @param array $array the array to extract value from + * @param string $key key name of the array element + * @param mixed $default the default value to be returned if the specified key does not exist + * @return mixed|null the value of the element if found, default value otherwise + */ + public static function remove(&$array, $key, $default = null) + { + if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { + $value = $array[$key]; + unset($array[$key]); + return $value; + } + return $default; + } + + /** + * 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 $descending whether to sort in descending or ascending order. When + * sorting by multiple keys with different descending orders, use an array of descending flags. + * @param integer|array $sortFlag the PHP sort flag. Valid values include + * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`. + * Please refer to [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. + * @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter + * is used only when `$sortFlag` is `SORT_STRING`. + * When sorting by multiple keys with different case sensitivities, use an array of boolean values. + * @throws InvalidParamException if the $descending or $sortFlag parameters do not have + * correct number of elements as that of $key. + */ + public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true) + { + $keys = is_array($key) ? $key : array($key); + if (empty($keys) || empty($array)) { + return; + } + $n = count($keys); + if (is_scalar($descending)) { + $descending = array_fill(0, $n, $descending); + } elseif (count($descending) !== $n) { + throw new InvalidParamException('The length of $descending 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 $sortFlag parameter must be the same as that of $keys.'); + } + if (is_scalar($caseSensitive)) { + $caseSensitive = array_fill(0, $n, $caseSensitive); + } elseif (count($caseSensitive) !== $n) { + throw new InvalidParamException('The length of $caseSensitive parameter must be the same as that of $keys.'); + } + $args = array(); + foreach ($keys as $i => $key) { + $flag = $sortFlag[$i]; + $cs = $caseSensitive[$i]; + if (!$cs && ($flag === SORT_STRING)) { + if (defined('SORT_FLAG_CASE')) { + $flag = $flag | SORT_FLAG_CASE; + $args[] = static::getColumn($array, $key); + } else { + $column = array(); + foreach (static::getColumn($array, $key) as $k => $value) { + $column[$k] = mb_strtolower($value); + } + $args[] = $column; + } + } else { + $args[] = static::getColumn($array, $key); + } + $args[] = $descending[$i] ? SORT_DESC : SORT_ASC; + $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; + } +} diff --git a/framework/yii/helpers/BaseConsole.php b/framework/yii/helpers/BaseConsole.php new file mode 100644 index 0000000..6796283 --- /dev/null +++ b/framework/yii/helpers/BaseConsole.php @@ -0,0 +1,835 @@ + + * @since 2.0 + */ +class BaseConsole +{ + 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 RESET = 0; + const NORMAL = 0; + 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"; + } + + /** + * Returns the ANSI format code. + * + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @return string The ANSI format code according to the given formatting constants. + */ + public static function ansiFormatCode($format) + { + return "\033[" . implode(';', $format) . 'm'; + } + + /** + * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards. + * + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @see ansiFormatCode() + * @see ansiFormatEnd() + */ + public static function beginAnsiFormat($format) + { + echo "\033[" . implode(';', $format) . 'm'; + } + + /** + * Resets any ANSI format set by previous method [[ansiFormatBegin()]] + * Any output after this will have default text format. + * This is equal to calling + * + * ```php + * echo Console::ansiFormatCode(array(Console::RESET)) + * ``` + */ + public static function endAnsiFormat() + { + echo "\033[0m"; + } + + /** + * Will return a string formatted with the given ANSI style + * + * @param string $string the string to be formatted + * @param array $format An array containing formatting values. + * You can pass any of the FG_*, BG_* and TEXT_* constants + * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. + * @return string + */ + public static function ansiFormat($string, $format = array()) + { + $code = implode(';', $format); + return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; + } + + /** + * Returns the ansi format code for xterm foreground color. + * You can pass the return value of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermFgColor($colorCode) + { + return '38;5;' . $colorCode; + } + + /** + * Returns the ansi format code for xterm background color. + * You can pass the return value of this to one of the formatting methods: + * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]] + * + * @param integer $colorCode xterm color code + * @return string + * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors + */ + public static function xtermBgColor($colorCode) + { + return '48;5;' . $colorCode; + } + + /** + * Strips ANSI control codes from a string + * + * @param string $string String to strip + * @return string + */ + public static function stripAnsiFormat($string) + { + return preg_replace('/\033\[[\d;?]*\w/', '', $string); + } + + /** + * Converts an ANSI formatted string to HTML + * @param $string + * @return mixed + */ + // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 + 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 self::FG_BLACK: + $style = array('color' => '#000000'); + break; + case self::FG_BLUE: + $style = array('color' => '#000078'); + break; + case self::FG_CYAN: + $style = array('color' => '#007878'); + break; + case self::FG_GREEN: + $style = array('color' => '#007800'); + break; + case self::FG_GREY: + $style = array('color' => '#787878'); + break; + case self::FG_PURPLE: + $style = array('color' => '#780078'); + break; + case self::FG_RED: + $style = array('color' => '#780000'); + break; + case self::FG_YELLOW: + $style = array('color' => '#787800'); + break; + case self::BG_BLACK: + $style = array('background-color' => '#000000'); + break; + case self::BG_BLUE: + $style = array('background-color' => '#000078'); + break; + case self::BG_CYAN: + $style = array('background-color' => '#007878'); + break; + case self::BG_GREEN: + $style = array('background-color' => '#007800'); + break; + case self::BG_GREY: + $style = array('background-color' => '#787878'); + break; + case self::BG_PURPLE: + $style = array('background-color' => '#780078'); + break; + case self::BG_RED: + $style = array('background-color' => '#780000'); + break; + case self::BG_YELLOW: + $style = array('background-color' => '#787800'); + break; + case self::BOLD: + $style = array('font-weight' => 'bold'); + break; + case self::ITALIC: + $style = array('font-style' => 'italic'); + break; + case self::UNDERLINE: + $style = array('text-decoration' => array('underline')); + break; + case self::OVERLINED: + $style = array('text-decoration' => array('overline')); + break; + case self::CROSSED_OUT: + $style = array('text-decoration' => array('line-through')); + break; + case self::BLINK: + $style = array('text-decoration' => array('blink')); + break; + case self::NEGATIVE: // ??? + case self::CONCEALED: + case self::ENCIRCLED: + case self::FRAMED: + // TODO allow resetting codes + break; + case 0: // ansi reset + $return = ''; + for ($n = $tags; $tags > 0; $tags--) { + $return .= ''; + } + 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 ' $value) { + echo " $key - $value\n"; + } + echo " ? - Show help\n"; + goto top; + } elseif (!in_array($input, array_keys($options))) { + goto top; + } + return $input; + } + + /** + * Displays and updates a simple progress bar on screen. + * + * @param integer $done the number of items that are completed + * @param integer $total the total value of items that are to be done + * @param integer $size the size of the status bar (optional) + * @see http://snipplr.com/view/29548/ + */ + public static function showProgress($done, $total, $size = 30) + { + static $start; + + // if we go over our bound, just ignore it + if ($done > $total) { + return; + } + + if (empty($start)) { + $start = time(); + } + + $now = time(); + + $percent = (double)($done / $total); + $bar = floor($percent * $size); + + $status = "\r["; + $status .= str_repeat("=", $bar); + if ($bar < $size) { + $status .= ">"; + $status .= str_repeat(" ", $size - $bar); + } else { + $status .= "="; + } + + $display = number_format($percent * 100, 0); + + $status .= "] $display% $done/$total"; + + $rate = ($now - $start) / $done; + $left = $total - $done; + $eta = round($rate * $left, 2); + + $elapsed = $now - $start; + + $status .= " remaining: " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec."; + + static::stdout("$status "); + + flush(); + + // when done, send a newline + if ($done == $total) { + echo "\n"; + } + } +} diff --git a/framework/yii/helpers/BaseFileHelper.php b/framework/yii/helpers/BaseFileHelper.php new file mode 100644 index 0000000..2c911b0 --- /dev/null +++ b/framework/yii/helpers/BaseFileHelper.php @@ -0,0 +1,329 @@ + + * @author Alex Makarov + * @since 2.0 + */ +class BaseFileHelper +{ + /** + * 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 the 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. + * + * @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); + finfo_close($info); + if ($result !== false) { + return $result; + } + } + } + + return $checkExtension ? static::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) + { + static $mimeTypes = array(); + if ($magicFile === null) { + $magicFile = __DIR__ . '/mimeTypes.php'; + } + if (!isset($mimeTypes[$magicFile])) { + $mimeTypes[$magicFile] = require($magicFile); + } + if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { + $ext = strtolower($ext); + if (isset($mimeTypes[$magicFile][$ext])) { + return $mimeTypes[$magicFile][$ext]; + } + } + return null; + } + + /** + * Copies a whole directory as another one. + * The files and sub-directories will also be copied over. + * @param string $src the source directory + * @param string $dst the destination directory + * @param array $options options for directory copy. Valid options are: + * + * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775. + * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. + * - filter: callback, a PHP callback that is called for each directory or file. + * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. + * The callback can return one of the following values: + * + * * true: the directory or file will be copied (the "only" and "except" options will be ignored) + * * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored) + * * null: the "only" and "except" options will determine whether the directory or file should be copied + * + * - only: array, list of patterns that the file paths should match if they want to be copied. + * A path matches a pattern if it contains the pattern string at its end. + * For example, '.php' matches all file paths ending with '.php'. + * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. + * If a file path matches a pattern in both "only" and "except", it will NOT be copied. + * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied. + * A path matches a pattern if it contains the pattern string at its end. + * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' + * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; + * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches + * both '/' and '\' in the paths. + * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true. + * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied. + * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or + * file copied from, while `$to` is the copy target. + */ + public static function copyDirectory($src, $dst, $options = array()) + { + if (!is_dir($dst)) { + static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true); + } + + $handle = opendir($src); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $from = $src . DIRECTORY_SEPARATOR . $file; + $to = $dst . DIRECTORY_SEPARATOR . $file; + if (static::filterPath($from, $options)) { + if (is_file($from)) { + copy($from, $to); + if (isset($options['fileMode'])) { + @chmod($to, $options['fileMode']); + } + } else { + static::copyDirectory($from, $to, $options); + } + if (isset($options['afterCopy'])) { + call_user_func($options['afterCopy'], $from, $to); + } + } + } + closedir($handle); + } + + /** + * Removes a directory (and all its content) recursively. + * @param string $dir the directory to be deleted recursively. + */ + public static function removeDirectory($dir) + { + if (!is_dir($dir) || !($handle = opendir($dir))) { + return; + } + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_file($path)) { + unlink($path); + } else { + static::removeDirectory($path); + } + } + closedir($handle); + rmdir($dir); + } + + /** + * Returns the files found under the specified directory and subdirectories. + * @param string $dir the directory under which the files will be looked for. + * @param array $options options for file searching. Valid options are: + * + * - filter: callback, a PHP callback that is called for each directory or file. + * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. + * The callback can return one of the following values: + * + * * true: the directory or file will be returned (the "only" and "except" options will be ignored) + * * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored) + * * null: the "only" and "except" options will determine whether the directory or file should be returned + * + * - only: array, list of patterns that the file paths should match if they want to be returned. + * A path matches a pattern if it contains the pattern string at its end. + * For example, '.php' matches all file paths ending with '.php'. + * Note, the '/' characters in a pattern matches both '/' and '\' in the paths. + * If a file path matches a pattern in both "only" and "except", it will NOT be returned. + * - except: array, list of patterns that the file paths or directory paths should match if they want to be excluded from the result. + * A path matches a pattern if it contains the pattern string at its end. + * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' + * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; + * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches + * both '/' and '\' in the paths. + * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. + * @return array files found under the directory. The file list is sorted. + */ + public static function findFiles($dir, $options = array()) + { + $list = array(); + $handle = opendir($dir); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (static::filterPath($path, $options)) { + if (is_file($path)) { + $list[] = $path; + } elseif (!isset($options['recursive']) || $options['recursive']) { + $list = array_merge($list, static::findFiles($path, $options)); + } + } + } + closedir($handle); + return $list; + } + + /** + * Checks if the given file path satisfies the filtering options. + * @param string $path the path of the file or directory to be checked + * @param array $options the filtering options. See [[findFiles()]] for explanations of + * the supported options. + * @return boolean whether the file or directory satisfies the filtering options. + */ + public static function filterPath($path, $options) + { + if (isset($options['filter'])) { + $result = call_user_func($options['filter'], $path); + if (is_bool($result)) { + return $result; + } + } + $path = str_replace('\\', '/', $path); + if ($isDir = is_dir($path)) { + $path .= '/'; + } + $n = StringHelper::strlen($path); + + if (!empty($options['except'])) { + foreach ($options['except'] as $name) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { + return false; + } + } + } + + if (!$isDir && !empty($options['only'])) { + foreach ($options['only'] as $name) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { + return true; + } + } + return false; + } + return true; + } + + /** + * Creates a new directory. + * + * This method is similar to the PHP `mkdir()` function except that + * it uses `chmod()` to set the permission of the created directory + * in order to avoid the impact of the `umask` setting. + * + * @param string $path path of the directory to be created. + * @param integer $mode the permission to be set for the created directory. + * @param boolean $recursive whether to create parent directories if they do not exist. + * @return boolean whether the directory is created successfully + */ + public static function createDirectory($path, $mode = 0775, $recursive = true) + { + if (is_dir($path)) { + return true; + } + $parentDir = dirname($path); + if ($recursive && !is_dir($parentDir)) { + static::createDirectory($parentDir, $mode, true); + } + $result = mkdir($path, $mode); + chmod($path, $mode); + return $result; + } +} diff --git a/framework/yii/helpers/BaseHtml.php b/framework/yii/helpers/BaseHtml.php new file mode 100644 index 0000000..2baa679 --- /dev/null +++ b/framework/yii/helpers/BaseHtml.php @@ -0,0 +1,1599 @@ + + * @since 2.0 + */ +class BaseHtml +{ + /** + * @var array list of void elements (element name => 1) + * @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 array the preferred order of attributes in a tag. This mainly affects the order of the attributes + * that are rendered by [[renderTagAttributes()]]. + */ + 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 + * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false, + * HTML entities in `$content` will not be further encoded. + * @return string the encoded content + * @see decode + * @see http://www.php.net/manual/en/function.htmlspecialchars.php + */ + public static function encode($content, $doubleEncode = true) + { + return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode); + } + + /** + * 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) . '>'; + return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content"; + } + + /** + * 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 ""; + } + + /** + * 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()) + { + return static::tag('style', $content, $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()) + { + return static::tag('script', $content, $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['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['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, such as "post", "get", "put", "delete" (case-insensitive). + * Since most browsers only support "post" and "get", if other methods are given, they will + * be simulated using "post", and a hidden input will be added which contains the actual method type. + * See [[\yii\web\Request::restVar]] for more details. + * @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); + + $hiddenInputs = array(); + + $request = Yii::$app->getRequest(); + if ($request instanceof Request) { + if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) { + // simulate PUT, DELETE, etc. via POST + $hiddenInputs[] = static::hiddenInput($request->restVar, $method); + $method = 'post'; + } + if ($request->enableCsrfValidation) { + $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken()); + } + } + + if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { + // query parameters in the action are ignored for GET method + // we use hidden fields to add them back + foreach (explode('&', substr($action, $pos + 1)) as $pair) { + if (($pos1 = strpos($pair, '=')) !== false) { + $hiddenInputs[] = static::hiddenInput( + urldecode(substr($pair, 0, $pos1)), + urldecode(substr($pair, $pos1 + 1)) + ); + } else { + $hiddenInputs[] = static::hiddenInput(urldecode($pair), ''); + } + } + $action = substr($action, 0, $pos); + } + + $options['action'] = $action; + $options['method'] = $method; + $form = static::beginTag('form', $options); + if (!empty($hiddenInputs)) { + $form .= "\n" . implode("\n", $hiddenInputs); + } + + return $form; + } + + /** + * Generates a form end tag. + * @return string the generated tag + * @see beginForm + */ + public static function endForm() + { + return ''; + } + + /** + * 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 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 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()) + { + $options['href'] = 'mailto:' . ($email === null ? $text : $email); + return static::tag('a', $text, $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', '', $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 [[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 $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 button tag + */ + public static function button($content = 'Button', $options = array()) + { + return static::tag('button', $content, $options); + } + + /** + * Generates a submit button tag. + * @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($content = 'Submit', $options = array()) + { + $options['type'] = 'submit'; + return static::button($content, $options); + } + + /** + * Generates a reset button tag. + * @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($content = 'Reset', $options = array()) + { + $options['type'] = 'reset'; + return static::button($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 === null ? null : (string)$value; + return static::tag('input', '', $options); + } + + /** + * Generates an input button. + * @param string $label 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($label = 'Button', $options = array()) + { + $options['type'] = 'button'; + $options['value'] = $label; + return static::tag('input', '', $options); + } + + /** + * Generates a submit input button. + * @param string $label 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($label = 'Submit', $options = array()) + { + $options['type'] = 'submit'; + $options['value'] = $label; + return static::tag('input', '', $options); + } + + /** + * Generates a reset input button. + * @param string $label 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($label = 'Reset', $options = array()) + { + $options['type'] = 'reset'; + $options['value'] = $label; + return static::tag('input', '', $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 array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - 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. + * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the radio button will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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, $options = array()) + { + $options['checked'] = (boolean)$checked; + $value = array_key_exists('value', $options) ? $options['value'] : '1'; + 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 = ''; + } + if (isset($options['label'])) { + $label = $options['label']; + $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); + unset($options['label'], $options['labelOptions']); + $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions); + return $hidden . static::tag('div', $content, array('class' => 'radio')); + } else { + 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 array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - 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. + * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the checkbox will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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, $options = array()) + { + $options['checked'] = (boolean)$checked; + $value = array_key_exists('value', $options) ? $options['value'] : '1'; + 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 = ''; + } + if (isset($options['label'])) { + $label = $options['label']; + $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : array(); + unset($options['label'], $options['labelOptions']); + $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions); + return $hidden . static::tag('div', $content, array('class' => 'checkbox')); + } else { + 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 specially handled: + * + * - 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 specially handled: + * + * - 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 (!empty($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. + * @param array $options options (name => config) for the checkbox list container tag. + * The following options are specially handled: + * + * - tag: string, the tag name of the container element. + * - 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. + * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. + * This option is ignored if `item` option is set. + * - 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, respectively. + * @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; + $encode = !isset($options['encode']) || $options['encode']; + $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::checkbox($name, $checked, array( + 'value' => $value, + 'label' => $encode ? static::encode($label) : $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"; + + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); + + return $hidden . static::tag($tag, implode($separator, $lines), $options); + } + + /** + * 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. + * @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. + * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true. + * This option is ignored if `item` option is set. + * - 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, respectively. + * @return string the generated radio button list + */ + public static function radioList($name, $selection = null, $items = array(), $options = array()) + { + $encode = !isset($options['encode']) || $options['encode']; + $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::radio($name, $checked, array( + 'value' => $value, + 'label' => $encode ? static::encode($label) : $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 = ''; + } + + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item']); + + return $hidden . static::tag($tag, implode($separator, $lines), $options); + } + + /** + * Generates an unordered list. + * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. + * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. + * @param array $options options (name => config) for the radio button list. The following options are supported: + * + * - encode: boolean, whether to HTML-encode the items. Defaults to true. + * This option is ignored if the `item` option is specified. + * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. + * - item: callable, a callback that is used to generate each individual list item. + * The signature of this callback must be: + * + * ~~~ + * function ($item, $index) + * ~~~ + * + * where $index is the array key corresponding to `$item` in `$items`. The callback should return + * the whole list item tag. + * + * @return string the generated unordered list. An empty string is returned if `$items` is empty. + */ + public static function ul($items, $options = array()) + { + if (empty($items)) { + return ''; + } + $tag = isset($options['tag']) ? $options['tag'] : 'ul'; + $encode = !isset($options['encode']) || $options['encode']; + $formatter = isset($options['item']) ? $options['item'] : null; + $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : array(); + unset($options['tag'], $options['encode'], $options['item'], $options['itemOptions']); + $results = array(); + foreach ($items as $index => $item) { + if ($formatter !== null) { + $results[] = call_user_func($formatter, $item, $index); + } else { + $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions); + } + } + return static::tag($tag, "\n" . implode("\n", $results) . "\n", $options); + } + + /** + * Generates an ordered list. + * @param array|\Traversable $items the items for generating the list. Each item generates a single list item. + * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true. + * @param array $options options (name => config) for the radio button list. The following options are supported: + * + * - encode: boolean, whether to HTML-encode the items. Defaults to true. + * This option is ignored if the `item` option is specified. + * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified. + * - item: callable, a callback that is used to generate each individual list item. + * The signature of this callback must be: + * + * ~~~ + * function ($item, $index) + * ~~~ + * + * where $index is the array key corresponding to `$item` in `$items`. The callback should return + * the whole list item tag. + * + * @return string the generated ordered list. An empty string is returned if `$items` is empty. + */ + public static function ol($items, $options = array()) + { + $options['tag'] = 'ol'; + return static::ul($items, $options); + } + + /** + * Generates a label tag for the given model attribute. + * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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. + * The following options are specially handled: + * + * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]]. + * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display + * (after encoding). + * + * @return string the generated label tag + */ + public static function activeLabel($model, $attribute, $options = array()) + { + $attribute = static::getAttributeName($attribute); + $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute)); + $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute); + unset($options['label'], $options['for']); + return static::label($label, $for, $options); + } + + /** + * Generates a tag that contains the first validation error of the specified model attribute. + * Note that even if there is no validation error, this method will still return an empty error tag. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded + * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. + * + * The following options are specially handled: + * + * - tag: this specifies the tag name. If not set, "div" will be used. + * + * @return string the generated label tag + */ + public static function error($model, $attribute, $options = array()) + { + $attribute = static::getAttributeName($attribute); + $error = $model->getFirstError($attribute); + $tag = isset($options['tag']) ? $options['tag'] : 'div'; + unset($options['tag']); + return Html::tag($tag, Html::encode($error), $options); + } + + /** + * Generates an input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param string $type the input type (e.g. 'text', 'password') + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeInput($type, $model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::input($type, $name, $value, $options); + } + + /** + * Generates a text input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeTextInput($model, $attribute, $options = array()) + { + return static::activeInput('text', $model, $attribute, $options); + } + + /** + * Generates a hidden input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeHiddenInput($model, $attribute, $options = array()) + { + return static::activeInput('hidden', $model, $attribute, $options); + } + + /** + * Generates a password input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activePasswordInput($model, $attribute, $options = array()) + { + return static::activeInput('password', $model, $attribute, $options); + } + + /** + * Generates a file input tag for the given model attribute. + * This method will generate the "name" and "value" tag attributes automatically for the model attribute + * unless they are explicitly specified in `$options`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated input tag + */ + public static function activeFileInput($model, $attribute, $options = array()) + { + // add a hidden field so that if a model only has a file field, we can + // still use isset($_POST[$modelClass]) to detect if the input is submitted + return static::activeHiddenInput($model, $attribute, array('id' => null, 'value' => '')) + . static::activeInput('file', $model, $attribute, $options); + } + + /** + * Generates a textarea tag for the given model attribute. + * The model attribute value will be used as the content in the textarea. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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()]]. + * @return string the generated textarea tag + */ + public static function activeTextarea($model, $attribute, $options = array()) + { + $name = static::getInputName($model, $attribute); + $value = static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::textarea($name, $value, $options); + } + + /** + * Generates a radio button tag for the given model attribute. + * This method will generate the "checked" tag attribute according to the model attribute value. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, + * it will take the default value '0'. This method will render a hidden input 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. + * - label: string, a label displayed next to the radio button. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the radio button will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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 activeRadio($model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('uncheck', $options)) { + $options['uncheck'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::radio($name, $checked, $options); + } + + /** + * Generates a checkbox tag for the given model attribute. + * This method will generate the "checked" tag attribute according to the model attribute value. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - uncheck: string, the value associated with the uncheck state of the radio button. If not set, + * it will take the default value '0'. This method will render a hidden input 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. + * - label: string, a label displayed next to the checkbox. 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 [[encode()]] it to prevent XSS attacks. + * When this option is specified, the checkbox will be enclosed by a label tag. + * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. + * + * 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 activeCheckbox($model, $attribute, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('uncheck', $options)) { + $options['uncheck'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::checkbox($name, $checked, $options); + } + + /** + * Generates a drop-down list for the given model attribute. + * The selection of the drop-down list is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeDropDownList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::dropDownList($name, $checked, $items, $options); + } + + /** + * Generates a list box. + * The selection of the list box is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeListBox($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::listBox($name, $checked, $items, $options); + } + + /** + * Generates a list of checkboxes. + * A checkbox list allows multiple selection, like [[listBox()]]. + * As a result, the corresponding submitted value is an array. + * The selection of the checkbox list is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeCheckboxList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::checkboxList($name, $checked, $items, $options); + } + + /** + * Generates a list of radio buttons. + * A radio button list is like a checkbox list, except that it only allows single selection. + * The selection of the radio buttons is taken from the value of the model attribute. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format + * about attribute expression. + * @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 specially handled: + * + * - 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 activeRadioList($model, $attribute, $items, $options = array()) + { + $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); + $checked = static::getAttributeValue($model, $attribute); + if (!array_key_exists('unselect', $options)) { + $options['unselect'] = '0'; + } + if (!array_key_exists('id', $options)) { + $options['id'] = static::getInputId($model, $attribute); + } + return static::radioList($name, $checked, $items, $options); + } + + /** + * 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'] = (string)$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. + * Attributes whose values are of boolean type will be treated as + * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes). + * And attributes whose values are null will not be rendered. + * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. + * @return string the rendering result. If the attributes are not empty, they will be rendered + * into a string with a leading white space (so 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 (is_bool($value)) { + if ($value) { + $html .= " $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 first be processed by [[Yii::getAlias()]]. If the result + * is an absolute URL, it will be returned without any change further; Otherwise, the result + * will be prefixed with [[\yii\web\Request::baseUrl]] 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')`. + * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used. + * + * @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 instanceof \yii\web\Controller) { + 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 { + $url = Yii::getAlias($url); + if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) { + return $url; + } else { + return Yii::$app->getRequest()->getBaseUrl() . '/' . $url; + } + } + } + + /** + * Adds a CSS class to the specified options. + * If the CSS class is already in the options, it will not be added again. + * @param array $options the options to be modified. + * @param string $class the CSS class to be added + */ + public static function addCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = ' ' . $options['class'] . ' '; + if (($pos = strpos($classes, ' ' . $class . ' ')) === false) { + $options['class'] .= ' ' . $class; + } + } else { + $options['class'] = $class; + } + } + + /** + * Removes a CSS class from the specified options. + * @param array $options the options to be modified. + * @param string $class the CSS class to be removed + */ + public static function removeCssClass(&$options, $class) + { + if (isset($options['class'])) { + $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY)); + if (($index = array_search($class, $classes)) !== false) { + unset($classes[$index]); + } + if (empty($classes)) { + unset($options['class']); + } else { + $options['class'] = implode(' ', $classes); + } + } + } + + /** + * Returns the real attribute name from the given attribute expression. + * + * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. + * It is mainly used in tabular data input and/or input of array type. Below are some examples: + * + * - `[0]content` is used in tabular data input to represent the "content" attribute + * for the first model in tabular input; + * - `dates[0]` represents the first array element of the "dates" attribute; + * - `[0]dates[0]` represents the first array element of the "dates" attribute + * for the first model in tabular input. + * + * If `$attribute` has neither prefix nor suffix, it will be returned back without change. + * @param string $attribute the attribute name or expression + * @return string the attribute name without prefix and suffix. + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getAttributeName($attribute) + { + if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + return $matches[2]; + } else { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + } + + /** + * Returns the value of the specified attribute name or expression. + * + * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`. + * See [[getAttributeName()]] for more details about attribute expression. + * + * @param Model $model the model object + * @param string $attribute the attribute name or expression + * @return mixed the corresponding attribute value + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getAttributeValue($model, $attribute) + { + if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + $attribute = $matches[2]; + $index = $matches[3]; + if ($index === '') { + return $model->$attribute; + } else { + $value = $model->$attribute; + foreach (explode('][', trim($index, '[]')) as $id) { + if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) { + $value = $value[$id]; + } else { + return null; + } + } + return $value; + } + } + + /** + * Generates an appropriate input name for the specified attribute name or expression. + * + * This method generates a name that can be used as the input name to collect user input + * for the specified attribute. The name is generated according to the [[Model::formName|form name]] + * of the model and the given attribute name. For example, if the form name of the `Post` model + * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`. + * + * See [[getAttributeName()]] for explanation of attribute expression. + * + * @param Model $model the model object + * @param string $attribute the attribute name or expression + * @return string the generated input name + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getInputName($model, $attribute) + { + $formName = $model->formName(); + if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) { + throw new InvalidParamException('Attribute name must contain word characters only.'); + } + $prefix = $matches[1]; + $attribute = $matches[2]; + $suffix = $matches[3]; + if ($formName === '' && $prefix === '') { + return $attribute . $suffix; + } elseif ($formName !== '') { + return $formName . $prefix . "[$attribute]" . $suffix; + } else { + throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.'); + } + } + + /** + * Generates an appropriate input ID for the specified attribute name or expression. + * + * This method converts the result [[getInputName()]] into a valid input ID. + * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`. + * @param Model $model the model object + * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression. + * @return string the generated input ID + * @throws InvalidParamException if the attribute name contains non-word characters. + */ + public static function getInputId($model, $attribute) + { + $name = strtolower(static::getInputName($model, $attribute)); + return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name); + } +} diff --git a/framework/yii/helpers/BaseHtmlPurifier.php b/framework/yii/helpers/BaseHtmlPurifier.php new file mode 100644 index 0000000..17d2122 --- /dev/null +++ b/framework/yii/helpers/BaseHtmlPurifier.php @@ -0,0 +1,34 @@ + + * @since 2.0 + */ +class BaseHtmlPurifier +{ + /** + * Passes markup through HTMLPurifier making it safe to output to end user + * + * @param string $content + * @param array|null $config + * @return string + */ + public static function process($content, $config = null) + { + $configInstance = \HTMLPurifier_Config::create($config); + $configInstance->autoFinalize = false; + $purifier=\HTMLPurifier::instance($configInstance); + $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath()); + return $purifier->purify($content); + } +} diff --git a/framework/yii/helpers/BaseInflector.php b/framework/yii/helpers/BaseInflector.php new file mode 100644 index 0000000..affd3dd --- /dev/null +++ b/framework/yii/helpers/BaseInflector.php @@ -0,0 +1,480 @@ + + * @since 2.0 + */ +class BaseInflector +{ + /** + * @var array the rules for converting a word into its plural form. + * The keys are the regular expressions and the values are the corresponding replacements. + */ + public static $plurals = array( + '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1', + '/^(sea[- ]bass)$/i' => '\1', + '/(m)ove$/i' => '\1oves', + '/(f)oot$/i' => '\1eet', + '/(h)uman$/i' => '\1umans', + '/(s)tatus$/i' => '\1tatuses', + '/(s)taff$/i' => '\1taff', + '/(t)ooth$/i' => '\1eeth', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ); + /** + * @var array the rules for converting a word into its singular form. + * The keys are the regular expressions and the values are the corresponding replacements. + */ + public static $singulars = array( + '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1', + '/^(sea[- ]bass)$/i' => '\1', + '/(s)tatuses$/i' => '\1tatus', + '/(f)eet$/i' => '\1oot', + '/(t)eeth$/i' => '\1ooth', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '', + ); + /** + * @var array the special rules for converting a word between its plural form and singular form. + * The keys are the special words in singular form, and the values are the corresponding plural form. + */ + public static $specials = array( + 'atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'curve' => 'curves', + 'foe' => 'foes', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs', + 'wave' => 'waves', + 'Amoyese' => 'Amoyese', + 'bison' => 'bison', + 'Borghese' => 'Borghese', + 'bream' => 'bream', + 'breeches' => 'breeches', + 'britches' => 'britches', + 'buffalo' => 'buffalo', + 'cantus' => 'cantus', + 'carp' => 'carp', + 'chassis' => 'chassis', + 'clippers' => 'clippers', + 'cod' => 'cod', + 'coitus' => 'coitus', + 'Congoese' => 'Congoese', + 'contretemps' => 'contretemps', + 'corps' => 'corps', + 'debris' => 'debris', + 'diabetes' => 'diabetes', + 'djinn' => 'djinn', + 'eland' => 'eland', + 'elk' => 'elk', + 'equipment' => 'equipment', + 'Faroese' => 'Faroese', + 'flounder' => 'flounder', + 'Foochowese' => 'Foochowese', + 'gallows' => 'gallows', + 'Genevese' => 'Genevese', + 'Genoese' => 'Genoese', + 'Gilbertese' => 'Gilbertese', + 'graffiti' => 'graffiti', + 'headquarters' => 'headquarters', + 'herpes' => 'herpes', + 'hijinks' => 'hijinks', + 'Hottentotese' => 'Hottentotese', + 'information' => 'information', + 'innings' => 'innings', + 'jackanapes' => 'jackanapes', + 'Kiplingese' => 'Kiplingese', + 'Kongoese' => 'Kongoese', + 'Lucchese' => 'Lucchese', + 'mackerel' => 'mackerel', + 'Maltese' => 'Maltese', + 'mews' => 'mews', + 'moose' => 'moose', + 'mumps' => 'mumps', + 'Nankingese' => 'Nankingese', + 'news' => 'news', + 'nexus' => 'nexus', + 'Niasese' => 'Niasese', + 'Pekingese' => 'Pekingese', + 'Piedmontese' => 'Piedmontese', + 'pincers' => 'pincers', + 'Pistoiese' => 'Pistoiese', + 'pliers' => 'pliers', + 'Portuguese' => 'Portuguese', + 'proceedings' => 'proceedings', + 'rabies' => 'rabies', + 'rice' => 'rice', + 'rhinoceros' => 'rhinoceros', + 'salmon' => 'salmon', + 'Sarawakese' => 'Sarawakese', + 'scissors' => 'scissors', + 'series' => 'series', + 'Shavese' => 'Shavese', + 'shears' => 'shears', + 'siemens' => 'siemens', + 'species' => 'species', + 'swine' => 'swine', + 'testes' => 'testes', + 'trousers' => 'trousers', + 'trout' => 'trout', + 'tuna' => 'tuna', + 'Vermontese' => 'Vermontese', + 'Wenchowese' => 'Wenchowese', + 'whiting' => 'whiting', + 'wildebeest' => 'wildebeest', + 'Yengeese' => 'Yengeese', + ); + /** + * @var array map of special chars and its translation. This is used by [[slug()]]. + */ + public static $transliteration = array( + '/ä|æ|ǽ/' => 'ae', + '/ö|œ/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', + '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', + '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', + '/ç|ć|ĉ|ċ|č/' => 'c', + '/Ð|Ď|Đ/' => 'D', + '/ð|ď|đ/' => 'd', + '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E', + '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e', + '/Ĝ|Ğ|Ġ|Ģ/' => 'G', + '/ĝ|ğ|ġ|ģ/' => 'g', + '/Ĥ|Ħ/' => 'H', + '/ĥ|ħ/' => 'h', + '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I', + '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i', + '/Ĵ/' => 'J', + '/ĵ/' => 'j', + '/Ķ/' => 'K', + '/ķ/' => 'k', + '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L', + '/ĺ|ļ|ľ|ŀ|ł/' => 'l', + '/Ñ|Ń|Ņ|Ň/' => 'N', + '/ñ|ń|ņ|ň|ʼn/' => 'n', + '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O', + '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o', + '/Ŕ|Ŗ|Ř/' => 'R', + '/ŕ|ŗ|ř/' => 'r', + '/Ś|Ŝ|Ş|Ș|Š/' => 'S', + '/ś|ŝ|ş|ș|š|ſ/' => 's', + '/Ţ|Ț|Ť|Ŧ/' => 'T', + '/ţ|ț|ť|ŧ/' => 't', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U', + '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u', + '/Ý|Ÿ|Ŷ/' => 'Y', + '/ý|ÿ|ŷ/' => 'y', + '/Ŵ/' => 'W', + '/ŵ/' => 'w', + '/Ź|Ż|Ž/' => 'Z', + '/ź|ż|ž/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/' => 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Œ/' => 'OE', + '/ƒ/' => 'f' + ); + + /** + * 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 $word the word to be pluralized + * @return string the pluralized word + */ + public static function pluralize($word) + { + if (isset(self::$specials[$word])) { + return self::$specials[$word]; + } + foreach (static::$plurals as $rule => $replacement) { + if (preg_match($rule, $word)) { + return preg_replace($rule, $replacement, $word); + } + } + return $word; + } + + /** + * Returns the singular of the $word + * @param string $word the english word to singularize + * @return string Singular noun. + */ + public static function singularize($word) + { + $result = array_search($word, self::$specials, true); + if ($result !== false) { + return $result; + } + foreach (static::$singulars as $rule => $replacement) { + if (preg_match($rule, $word)) { + return preg_replace($rule, $replacement, $word); + } + } + return $word; + } + + /** + * Converts an underscored or CamelCase word into a English + * sentence. + * @param string $words + * @param bool $ucAll whether to set all words to uppercase + * @return string + */ + public static function titleize($words, $ucAll = false) + { + $words = static::humanize(static::underscore($words), $ucAll); + return $ucAll ? ucwords($words) : ucfirst($words); + } + + /** + * Returns given word as CamelCased + * Converts a word like "send_email" to "SendEmail". It + * will remove non alphanumeric character from the word, so + * "who's online" will be converted to "WhoSOnline" + * @see variablize + * @param string $word the word to CamelCase + * @return string + */ + public static function camelize($word) + { + return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); + } + + /** + * 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('/(? ' ', + '/\\s+/' => $replacement, + '/(?<=[a-z])([A-Z])/' => $replacement . '\\1', + str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => '' + ); + return preg_replace(array_keys($map), array_values($map), $string); + } + + /** + * Converts a table name to its class name. For example, converts "people" to "Person" + * @param string $tableName + * @return string + */ + public static function classify($tableName) + { + return static::camelize(static::singularize($tableName)); + } + + /** + * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ... + * @param int $number the number to get its ordinal value + * @return string + */ + public static function ordinalize($number) + { + if (in_array(($number % 100), range(11, 13))) { + return $number . 'th'; + } + switch ($number % 10) { + case 1: return $number . 'st'; + case 2: return $number . 'nd'; + case 3: return $number . 'rd'; + default: return $number . 'th'; + } + } +} diff --git a/framework/yii/helpers/BaseJson.php b/framework/yii/helpers/BaseJson.php new file mode 100644 index 0000000..bd6ede5 --- /dev/null +++ b/framework/yii/helpers/BaseJson.php @@ -0,0 +1,112 @@ + + * @since 2.0 + */ +class BaseJson +{ + /** + * Encodes the given value into a JSON string. + * The method enhances `json_encode()` by supporting JavaScript expressions. + * In particular, the method will not encode a JavaScript expression that is + * represented in terms of a [[JsExpression]] object. + * @param mixed $value the data to be encoded + * @param integer $options the encoding options. For more details please refer to + * [[http://www.php.net/manual/en/function.json-encode.php]] + * @return string the encoding result + */ + public static function encode($value, $options = 0) + { + $expressions = array(); + $value = static::processData($value, $expressions, uniqid()); + $json = json_encode($value, $options); + return empty($expressions) ? $json : strtr($json, $expressions); + } + + /** + * Decodes the given JSON string into a PHP data structure. + * @param string $json the JSON string to be decoded + * @param boolean $asArray whether to return objects in terms of associative arrays. + * @return mixed the PHP data + * @throws InvalidParamException if there is any decoding error + */ + public static function decode($json, $asArray = true) + { + if (is_array($json)) { + throw new InvalidParamException('Invalid JSON data.'); + } + $decode = json_decode((string)$json, $asArray); + switch (json_last_error()) { + case JSON_ERROR_NONE: + break; + case JSON_ERROR_DEPTH: + throw new InvalidParamException('The maximum stack depth has been exceeded.'); + case JSON_ERROR_CTRL_CHAR: + throw new InvalidParamException('Control character error, possibly incorrectly encoded.'); + case JSON_ERROR_SYNTAX: + throw new InvalidParamException('Syntax error.'); + case JSON_ERROR_STATE_MISMATCH: + throw new InvalidParamException('Invalid or malformed JSON.'); + case JSON_ERROR_UTF8: + throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.'); + default: + throw new InvalidParamException('Unknown JSON decoding error.'); + } + + return $decode; + } + + /** + * Pre-processes the data before sending it to `json_encode()`. + * @param mixed $data the data to be processed + * @param array $expressions collection of JavaScript expressions + * @param string $expPrefix a prefix internally used to handle JS expressions + * @return mixed the processed data + */ + protected static function processData($data, &$expressions, $expPrefix) + { + if (is_array($data)) { + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $data[$key] = static::processData($value, $expressions, $expPrefix); + } + } + return $data; + } elseif (is_object($data)) { + if ($data instanceof JsExpression) { + $token = "!{[$expPrefix=" . count($expressions) . ']}!'; + $expressions['"' . $token . '"'] = $data->expression; + return $token; + } else { + $data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data); + $result = array(); + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $result[$key] = static::processData($value, $expressions, $expPrefix); + } else { + $result[$key] = $value; + } + } + return $result; + } + } else { + return $data; + } + } +} diff --git a/framework/yii/helpers/BaseMarkdown.php b/framework/yii/helpers/BaseMarkdown.php new file mode 100644 index 0000000..40a1dc4 --- /dev/null +++ b/framework/yii/helpers/BaseMarkdown.php @@ -0,0 +1,44 @@ + + * @since 2.0 + */ +class BaseMarkdown +{ + /** + * @var MarkdownExtra + */ + protected static $markdown; + + /** + * Converts markdown into HTML + * + * @param string $content + * @param array $config + * @return string + */ + public static function process($content, $config = array()) + { + if (static::$markdown === null) { + static::$markdown = new MarkdownExtra(); + } + foreach ($config as $name => $value) { + static::$markdown->{$name} = $value; + } + return static::$markdown->transform($content); + } +} diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php new file mode 100644 index 0000000..3be07b4 --- /dev/null +++ b/framework/yii/helpers/BaseSecurity.php @@ -0,0 +1,285 @@ + + * @author Tom Worster + * @since 2.0 + */ +class BaseSecurity +{ + /** + * 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(); + // 192-bit (24 bytes) key size + $key = StringHelper::substr($key, 0, 24); + 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(); + // 192-bit (24 bytes) key size + $key = StringHelper::substr($key, 0, 24); + $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 = array(); + if (is_file($keyFile)) { + $keys = require($keyFile); + } + } + if (!isset($keys[$name])) { + $keys[$name] = static::generateRandomKey($length); + file_put_contents($keyFile, " 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 > 31) { + 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; + } +} diff --git a/framework/yii/helpers/BaseStringHelper.php b/framework/yii/helpers/BaseStringHelper.php new file mode 100644 index 0000000..e1622b9 --- /dev/null +++ b/framework/yii/helpers/BaseStringHelper.php @@ -0,0 +1,138 @@ + + * @author Alex Makarov + * @since 2.0 + */ +class BaseStringHelper +{ + /** + * Returns the number of bytes in the given string. + * This method ensures the string is treated as a byte array by using `mb_strlen()`. + * @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 mb_strlen($string, '8bit'); + } + + /** + * Returns the portion of string specified by the start and length parameters. + * This method ensures the string is treated as a byte array by using `mb_substr()`. + * @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 mb_substr($string, $start, $length, '8bit'); + } + + /** + * Returns the trailing name component of a path. + * This method is similar to the php function `basename()` except that it will + * treat both \ and / as directory separators, independent of the operating system. + * This method was mainly created to work on php namespaces. When working with real + * file paths, php's `basename()` should work fine for you. + * Note: this method is not aware of the actual filesystem, or path components such as "..". + * @param string $path A path string. + * @param string $suffix If the name component ends in suffix this will also be cut off. + * @return string the trailing name component of the given path. + * @see http://www.php.net/manual/en/function.basename.php + */ + public static function basename($path, $suffix = '') + { + if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) { + $path = mb_substr($path, 0, -$len); + } + $path = rtrim(str_replace('\\', '/', $path), '/\\'); + if (($pos = mb_strrpos($path, '/')) !== false) { + return mb_substr($path, $pos + 1); + } + return $path; + } + + /** + * Returns parent directory's path. + * This method is similar to `dirname()` except that it will treat + * both \ and / as directory separators, independent of the operating system. + * @param string $path A path string. + * @return string the parent directory's path. + * @see http://www.php.net/manual/en/function.basename.php + */ + public static function dirname($path) + { + $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); + if ($pos !== false) { + return mb_substr($path, 0, $pos); + } else { + return $path; + } + } + + /** + * Compares two strings or string arrays, and return their differences. + * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package. + * @param string|array $lines1 the first string or string array to be compared. If it is a string, + * it will be converted into a string array by breaking at newlines. + * @param string|array $lines2 the second string or string array to be compared. If it is a string, + * it will be converted into a string array by breaking at newlines. + * @param string $format the output format. It must be 'inline', 'unified', 'context', 'side-by-side', or 'array'. + * @return string|array the comparison result. An array is returned if `$format` is 'array'. For all other + * formats, a string is returned. + * @throws InvalidParamException if the format is invalid. + */ + public static function diff($lines1, $lines2, $format = 'inline') + { + if (!is_array($lines1)) { + $lines1 = explode("\n", $lines1); + } + if (!is_array($lines2)) { + $lines2 = explode("\n", $lines2); + } + foreach ($lines1 as $i => $line) { + $lines1[$i] = rtrim($line, "\r\n"); + } + foreach ($lines2 as $i => $line) { + $lines2[$i] = rtrim($line, "\r\n"); + } + switch ($format) { + case 'inline': + $renderer = new \Diff_Renderer_Html_Inline(); + break; + case 'array': + $renderer = new \Diff_Renderer_Html_Array(); + break; + case 'side-by-side': + $renderer = new \Diff_Renderer_Html_SideBySide(); + break; + case 'context': + $renderer = new \Diff_Renderer_Text_Context(); + break; + case 'unified': + $renderer = new \Diff_Renderer_Text_Unified(); + break; + default: + throw new InvalidParamException("Output format must be 'inline', 'side-by-side', 'array', 'context' or 'unified'."); + } + $diff = new \Diff($lines1, $lines2); + return $diff->render($renderer); + } +} diff --git a/framework/yii/helpers/BaseVarDumper.php b/framework/yii/helpers/BaseVarDumper.php new file mode 100644 index 0000000..f109125 --- /dev/null +++ b/framework/yii/helpers/BaseVarDumper.php @@ -0,0 +1,127 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\helpers; + +/** + * BaseVarDumper provides concrete implementation for [[VarDumper]]. + * + * Do not use BaseVarDumper. Use [[VarDumper]] instead. + * + * @author Qiang Xue + * @since 2.0 + */ +class BaseVarDumper +{ + 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 static::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("/', '', $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 = array_push(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; + } + } +} diff --git a/framework/yii/helpers/Console.php b/framework/yii/helpers/Console.php index 9b0656e..a34dc96 100644 --- a/framework/yii/helpers/Console.php +++ b/framework/yii/helpers/Console.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Carsten Brandt * @since 2.0 */ -class Console extends AbstractConsole +class Console extends BaseConsole { } diff --git a/framework/yii/helpers/FileHelper.php b/framework/yii/helpers/FileHelper.php index 919dc09..63954a4 100644 --- a/framework/yii/helpers/FileHelper.php +++ b/framework/yii/helpers/FileHelper.php @@ -16,6 +16,6 @@ namespace yii\helpers; * @author Alex Makarov * @since 2.0 */ -class FileHelper extends AbstractFileHelper +class FileHelper extends BaseFileHelper { } diff --git a/framework/yii/helpers/Html.php b/framework/yii/helpers/Html.php index 0715c6c..f4fbbba 100644 --- a/framework/yii/helpers/Html.php +++ b/framework/yii/helpers/Html.php @@ -13,6 +13,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class Html extends AbstractHtml +class Html extends BaseHtml { } diff --git a/framework/yii/helpers/HtmlPurifier.php b/framework/yii/helpers/HtmlPurifier.php index ca7e485..e1511e4 100644 --- a/framework/yii/helpers/HtmlPurifier.php +++ b/framework/yii/helpers/HtmlPurifier.php @@ -32,6 +32,6 @@ namespace yii\helpers; * @author Alexander Makarov * @since 2.0 */ -class HtmlPurifier extends AbstractHtmlPurifier +class HtmlPurifier extends BaseHtmlPurifier { } diff --git a/framework/yii/helpers/Inflector.php b/framework/yii/helpers/Inflector.php index 71e7f05..ab4713e 100644 --- a/framework/yii/helpers/Inflector.php +++ b/framework/yii/helpers/Inflector.php @@ -13,6 +13,6 @@ namespace yii\helpers; * @author Antonio Ramirez * @since 2.0 */ -class Inflector extends AbstractInflector +class Inflector extends BaseInflector { } diff --git a/framework/yii/helpers/Json.php b/framework/yii/helpers/Json.php index 8544a61..8ca436a 100644 --- a/framework/yii/helpers/Json.php +++ b/framework/yii/helpers/Json.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class Json extends AbstractJson +class Json extends BaseJson { } diff --git a/framework/yii/helpers/Markdown.php b/framework/yii/helpers/Markdown.php index 89f8801..3dcc750 100644 --- a/framework/yii/helpers/Markdown.php +++ b/framework/yii/helpers/Markdown.php @@ -30,6 +30,6 @@ namespace yii\helpers; * @author Alexander Makarov * @since 2.0 */ -class Markdown extends AbstractMarkdown +class Markdown extends BaseMarkdown { } diff --git a/framework/yii/helpers/Security.php b/framework/yii/helpers/Security.php index e2c0314..0e3ee38 100644 --- a/framework/yii/helpers/Security.php +++ b/framework/yii/helpers/Security.php @@ -24,6 +24,6 @@ namespace yii\helpers; * @author Tom Worster * @since 2.0 */ -class Security extends AbstractSecurity +class Security extends BaseSecurity { } diff --git a/framework/yii/helpers/StringHelper.php b/framework/yii/helpers/StringHelper.php index e367c59..5ecd390 100644 --- a/framework/yii/helpers/StringHelper.php +++ b/framework/yii/helpers/StringHelper.php @@ -14,6 +14,6 @@ namespace yii\helpers; * @author Alex Makarov * @since 2.0 */ -class StringHelper extends AbstractStringHelper +class StringHelper extends BaseStringHelper { } diff --git a/framework/yii/helpers/VarDumper.php b/framework/yii/helpers/VarDumper.php index 0aae16e..1ac5aa7 100644 --- a/framework/yii/helpers/VarDumper.php +++ b/framework/yii/helpers/VarDumper.php @@ -23,6 +23,6 @@ namespace yii\helpers; * @author Qiang Xue * @since 2.0 */ -class VarDumper extends AbstractVarDumper +class VarDumper extends BaseVarDumper { } diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php index 76354d4..2046ecc 100644 --- a/framework/yii/log/Logger.php +++ b/framework/yii/log/Logger.php @@ -220,7 +220,7 @@ class Logger extends Component * Returns the total elapsed time since the start of the current request. * This method calculates the difference between now and the timestamp * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning - * of [[AbstractYii]] class file. + * of [[BaseYii]] class file. * @return float the total elapsed time in seconds for current request. */ public function getElapsedTime()