Alexander Makarov
12 years ago
74 changed files with 4698 additions and 3356 deletions
@ -0,0 +1,9 @@
|
||||
<?php |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
|
||||
require(__DIR__ . '/../framework/yii.php'); |
||||
|
||||
$config = require(__DIR__ . '/protected/config/main.php'); |
||||
$application = new yii\web\Application($config); |
||||
$application->run(); |
@ -0,0 +1,15 @@
|
||||
<?php |
||||
|
||||
return array( |
||||
'id' => 'hello', |
||||
'basePath' => dirname(__DIR__), |
||||
'components' => array( |
||||
'cache' => array( |
||||
'class' => 'yii\caching\FileCache', |
||||
), |
||||
'user' => array( |
||||
'class' => 'yii\web\User', |
||||
'identityClass' => 'app\models\User', |
||||
) |
||||
), |
||||
); |
@ -0,0 +1,22 @@
|
||||
<?php |
||||
|
||||
class SiteController extends \yii\web\Controller |
||||
{ |
||||
public function actionIndex() |
||||
{ |
||||
echo $this->render('index'); |
||||
} |
||||
|
||||
public function actionLogin() |
||||
{ |
||||
$user = app\models\User::findIdentity(100); |
||||
Yii::$app->getUser()->login($user); |
||||
Yii::$app->getResponse()->redirect(array('site/index')); |
||||
} |
||||
|
||||
public function actionLogout() |
||||
{ |
||||
Yii::$app->getUser()->logout(); |
||||
Yii::$app->getResponse()->redirect(array('site/index')); |
||||
} |
||||
} |
@ -0,0 +1,43 @@
|
||||
<?php |
||||
|
||||
namespace app\models; |
||||
|
||||
class User extends \yii\base\Object implements \yii\web\Identity |
||||
{ |
||||
public $id; |
||||
public $name; |
||||
public $authKey; |
||||
|
||||
private static $users = array( |
||||
'100' => array( |
||||
'id' => '100', |
||||
'authKey' => 'test100key', |
||||
'name' => 'admin', |
||||
), |
||||
'101' => array( |
||||
'id' => '101', |
||||
'authKey' => 'test101key', |
||||
'name' => 'demo', |
||||
), |
||||
); |
||||
|
||||
public static function findIdentity($id) |
||||
{ |
||||
return isset(self::$users[$id]) ? new self(self::$users[$id]) : null; |
||||
} |
||||
|
||||
public function getId() |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
public function getAuthKey() |
||||
{ |
||||
return $this->authKey; |
||||
} |
||||
|
||||
public function validateAuthKey($authKey) |
||||
{ |
||||
return $this->authKey === $authKey; |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
<?php |
||||
/** |
||||
* @var $this \yii\base\View |
||||
* @var $content string |
||||
*/ |
||||
use yii\helpers\Html; |
||||
?> |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<?php $this->beginPage(); ?> |
||||
<head> |
||||
<title><?php echo Html::encode($this->title); ?></title>
|
||||
<?php $this->head(); ?> |
||||
</head> |
||||
<body> |
||||
<h1>Welcome</h1> |
||||
<?php $this->beginBody(); ?> |
||||
<?php echo $content; ?> |
||||
<?php $this->endBody(); ?> |
||||
</body> |
||||
<?php $this->endPage(); ?> |
||||
</html> |
@ -0,0 +1,17 @@
|
||||
<?php |
||||
/** @var $this \yii\base\View */ |
||||
|
||||
use yii\helpers\Html; |
||||
|
||||
$this->title = 'Hello World'; |
||||
|
||||
$user = Yii::$app->getUser(); |
||||
if ($user->isGuest) { |
||||
echo Html::a('login', array('login')); |
||||
} else { |
||||
echo "You are logged in as " . $user->identity->name . "<br/>"; |
||||
echo Html::a('logout', array('logout')); |
||||
} |
||||
?> |
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
<?php |
||||
|
||||
return array( |
||||
'basePath' => __DIR__ . '/web/assets', |
||||
); |
@ -0,0 +1,26 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\base; |
||||
|
||||
/** |
||||
* UnknownClassException represents an exception caused by accessing an unknown class. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class UnknownClassException extends Exception |
||||
{ |
||||
/** |
||||
* @return string the user-friendly name of this exception |
||||
*/ |
||||
public function getName() |
||||
{ |
||||
return \Yii::t('yii|Unknown Class'); |
||||
} |
||||
} |
||||
|
@ -1,136 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\base; |
||||
|
||||
/** |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class ViewContent extends Component |
||||
{ |
||||
const POS_HEAD = 1; |
||||
const POS_BEGIN = 2; |
||||
const POS_END = 3; |
||||
|
||||
/** |
||||
* @var array |
||||
* |
||||
* Each asset bundle should be declared with the following structure: |
||||
* |
||||
* ~~~ |
||||
* array( |
||||
* 'basePath' => '...', |
||||
* 'baseUrl' => '...', // if missing, the bundle will be published to the "www/assets" folder |
||||
* 'js' => array( |
||||
* 'js/main.js', |
||||
* 'js/menu.js', |
||||
* 'js/base.js' => self::POS_HEAD, |
||||
* 'css' => array( |
||||
* 'css/main.css', |
||||
* 'css/menu.css', |
||||
* ), |
||||
* 'depends' => array( |
||||
* 'jquery', |
||||
* 'yii', |
||||
* 'yii/treeview', |
||||
* ), |
||||
* ) |
||||
* ~~~ |
||||
*/ |
||||
public $bundles; |
||||
|
||||
public $title; |
||||
public $metaTags; |
||||
public $linkTags; |
||||
public $css; |
||||
public $js; |
||||
public $cssFiles; |
||||
public $jsFiles; |
||||
|
||||
public function populate($content) |
||||
{ |
||||
return $content; |
||||
} |
||||
|
||||
public function reset() |
||||
{ |
||||
$this->title = null; |
||||
$this->metaTags = null; |
||||
$this->linkTags = null; |
||||
$this->css = null; |
||||
$this->js = null; |
||||
$this->cssFiles = null; |
||||
$this->jsFiles = null; |
||||
} |
||||
|
||||
public function registerBundle($name) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public function getMetaTag($key) |
||||
{ |
||||
return isset($this->metaTags[$key]) ? $this->metaTags[$key] : null; |
||||
} |
||||
|
||||
public function setMetaTag($key, $tag) |
||||
{ |
||||
$this->metaTags[$key] = $tag; |
||||
} |
||||
|
||||
public function getLinkTag($key) |
||||
{ |
||||
return isset($this->linkTags[$key]) ? $this->linkTags[$key] : null; |
||||
} |
||||
|
||||
public function setLinkTag($key, $tag) |
||||
{ |
||||
$this->linkTags[$key] = $tag; |
||||
} |
||||
|
||||
public function getCss($key) |
||||
{ |
||||
return isset($this->css[$key]) ? $this->css[$key]: null; |
||||
} |
||||
|
||||
public function setCss($key, $css) |
||||
{ |
||||
$this->css[$key] = $css; |
||||
} |
||||
|
||||
public function getCssFile($key) |
||||
{ |
||||
return isset($this->cssFiles[$key]) ? $this->cssFiles[$key]: null; |
||||
} |
||||
|
||||
public function setCssFile($key, $file) |
||||
{ |
||||
$this->cssFiles[$key] = $file; |
||||
} |
||||
|
||||
public function getJs($key, $position = self::POS_END) |
||||
{ |
||||
return isset($this->js[$position][$key]) ? $this->js[$position][$key] : null; |
||||
} |
||||
|
||||
public function setJs($key, $js, $position = self::POS_END) |
||||
{ |
||||
$this->js[$position][$key] = $js; |
||||
} |
||||
|
||||
public function getJsFile($key, $position = self::POS_END) |
||||
{ |
||||
return isset($this->jsFiles[$position][$key]) ? $this->jsFiles[$position][$key] : null; |
||||
} |
||||
|
||||
public function setJsFile($key, $file, $position = self::POS_END) |
||||
{ |
||||
$this->jsFiles[$position][$key] = $file; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,353 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\console\controllers; |
||||
|
||||
use Yii; |
||||
use yii\console\Exception; |
||||
use yii\console\Controller; |
||||
|
||||
/** |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class AssetController extends Controller |
||||
{ |
||||
public $defaultAction = 'compress'; |
||||
|
||||
public $bundles = array(); |
||||
public $extensions = array(); |
||||
/** |
||||
* @var array |
||||
* ~~~ |
||||
* 'all' => array( |
||||
* 'css' => 'all.css', |
||||
* 'js' => 'js.css', |
||||
* 'depends' => array( ... ), |
||||
* ) |
||||
* ~~~ |
||||
*/ |
||||
public $targets = array(); |
||||
public $assetManager = array(); |
||||
public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}'; |
||||
public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; |
||||
|
||||
public function actionCompress($configFile, $bundleFile) |
||||
{ |
||||
$this->loadConfiguration($configFile); |
||||
$bundles = $this->loadBundles($this->bundles, $this->extensions); |
||||
$targets = $this->loadTargets($this->targets, $bundles); |
||||
$this->publishBundles($bundles, $this->publishOptions); |
||||
$timestamp = time(); |
||||
foreach ($targets as $target) { |
||||
if (!empty($target->js)) { |
||||
$this->buildTarget($target, 'js', $bundles, $timestamp); |
||||
} |
||||
if (!empty($target->css)) { |
||||
$this->buildTarget($target, 'css', $bundles, $timestamp); |
||||
} |
||||
} |
||||
|
||||
$targets = $this->adjustDependency($targets, $bundles); |
||||
$this->saveTargets($targets, $bundleFile); |
||||
} |
||||
|
||||
protected function loadConfiguration($configFile) |
||||
{ |
||||
foreach (require($configFile) as $name => $value) { |
||||
if (property_exists($this, $name)) { |
||||
$this->$name = $value; |
||||
} else { |
||||
throw new Exception("Unknown configuration option: $name"); |
||||
} |
||||
} |
||||
|
||||
if (!isset($this->assetManager['basePath'])) { |
||||
throw new Exception("Please specify 'basePath' for the 'assetManager' option."); |
||||
} |
||||
if (!isset($this->assetManager['baseUrl'])) { |
||||
throw new Exception("Please specify 'baseUrl' for the 'assetManager' option."); |
||||
} |
||||
} |
||||
|
||||
protected function loadBundles($bundles, $extensions) |
||||
{ |
||||
$result = array(); |
||||
foreach ($bundles as $name => $bundle) { |
||||
$bundle['class'] = 'yii\\web\\AssetBundle'; |
||||
$result[$name] = Yii::createObject($bundle); |
||||
} |
||||
foreach ($extensions as $path) { |
||||
$manifest = $path . '/assets.php'; |
||||
if (!is_file($manifest)) { |
||||
continue; |
||||
} |
||||
foreach (require($manifest) as $name => $bundle) { |
||||
if (!isset($result[$name])) { |
||||
$bundle['class'] = 'yii\\web\\AssetBundle'; |
||||
$result[$name] = Yii::createObject($bundle); |
||||
} |
||||
} |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
protected function loadTargets($targets, $bundles) |
||||
{ |
||||
// build the dependency order of bundles |
||||
$registered = array(); |
||||
foreach ($bundles as $name => $bundle) { |
||||
$this->registerBundle($bundles, $name, $registered); |
||||
} |
||||
$bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1)); |
||||
|
||||
// fill up the target which has empty 'depends'. |
||||
$referenced = array(); |
||||
foreach ($targets as $name => $target) { |
||||
if (empty($target['depends'])) { |
||||
if (!isset($all)) { |
||||
$all = $name; |
||||
} else { |
||||
throw new Exception("Only one target can have empty 'depends' option. Found two now: $all, $name"); |
||||
} |
||||
} else { |
||||
foreach ($target['depends'] as $bundle) { |
||||
if (!isset($referenced[$bundle])) { |
||||
$referenced[$bundle] = $name; |
||||
} else { |
||||
throw new Exception("Target '{$referenced[$bundle]}' and '$name' cannot contain the bundle '$bundle' at the same time."); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (isset($all)) { |
||||
$targets[$all]['depends'] = array_diff(array_keys($registered), array_keys($referenced)); |
||||
} |
||||
|
||||
// adjust the 'depends' order for each target according to the dependency order of bundles |
||||
// create an AssetBundle object for each target |
||||
foreach ($targets as $name => $target) { |
||||
if (!isset($target['basePath'])) { |
||||
throw new Exception("Please specify 'basePath' for the '$name' target."); |
||||
} |
||||
if (!isset($target['baseUrl'])) { |
||||
throw new Exception("Please specify 'baseUrl' for the '$name' target."); |
||||
} |
||||
usort($target['depends'], function ($a, $b) use ($bundleOrders) { |
||||
if ($bundleOrders[$a] == $bundleOrders[$b]) { |
||||
return 0; |
||||
} else { |
||||
return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1; |
||||
} |
||||
}); |
||||
$target['class'] = 'yii\\web\\AssetBundle'; |
||||
$targets[$name] = Yii::createObject($target); |
||||
} |
||||
return $targets; |
||||
} |
||||
|
||||
/** |
||||
* @param \yii\web\AssetBundle[] $bundles |
||||
* @param array $options |
||||
*/ |
||||
protected function publishBundles($bundles, $options) |
||||
{ |
||||
if (!isset($options['class'])) { |
||||
$options['class'] = 'yii\\web\\AssetManager'; |
||||
} |
||||
$am = Yii::createObject($options); |
||||
foreach ($bundles as $bundle) { |
||||
$bundle->publish($am); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param \yii\web\AssetBundle $target |
||||
* @param string $type either "js" or "css" |
||||
* @param \yii\web\AssetBundle[] $bundles |
||||
* @param integer $timestamp |
||||
* @throws Exception |
||||
*/ |
||||
protected function buildTarget($target, $type, $bundles, $timestamp) |
||||
{ |
||||
$outputFile = strtr($target->$type, array( |
||||
'{ts}' => $timestamp, |
||||
)); |
||||
$inputFiles = array(); |
||||
|
||||
foreach ($target->depends as $name) { |
||||
if (isset($bundles[$name])) { |
||||
foreach ($bundles[$name]->$type as $file) { |
||||
$inputFiles[] = $bundles[$name]->basePath . '/' . $file; |
||||
} |
||||
} else { |
||||
throw new Exception("Unknown bundle: $name"); |
||||
} |
||||
} |
||||
if ($type === 'js') { |
||||
$this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile); |
||||
} else { |
||||
$this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile); |
||||
} |
||||
$target->$type = array($outputFile); |
||||
} |
||||
|
||||
protected function adjustDependency($targets, $bundles) |
||||
{ |
||||
$map = array(); |
||||
foreach ($targets as $name => $target) { |
||||
foreach ($target->depends as $bundle) { |
||||
$map[$bundle] = $name; |
||||
} |
||||
} |
||||
|
||||
foreach ($targets as $name => $target) { |
||||
$depends = array(); |
||||
foreach ($target->depends as $bn) { |
||||
foreach ($bundles[$bn]->depends as $bundle) { |
||||
$depends[$map[$bundle]] = true; |
||||
} |
||||
} |
||||
unset($depends[$name]); |
||||
$target->depends = array_keys($depends); |
||||
} |
||||
|
||||
// detect possible circular dependencies |
||||
foreach ($targets as $name => $target) { |
||||
$registered = array(); |
||||
$this->registerBundle($targets, $name, $registered); |
||||
} |
||||
|
||||
foreach ($map as $bundle => $target) { |
||||
$targets[$bundle] = Yii::createObject(array( |
||||
'class' => 'yii\\web\\AssetBundle', |
||||
'depends' => array($target), |
||||
)); |
||||
} |
||||
return $targets; |
||||
} |
||||
|
||||
protected function registerBundle($bundles, $name, &$registered) |
||||
{ |
||||
if (!isset($registered[$name])) { |
||||
$registered[$name] = false; |
||||
$bundle = $bundles[$name]; |
||||
foreach ($bundle->depends as $depend) { |
||||
$this->registerBundle($bundles, $depend, $registered); |
||||
} |
||||
unset($registered[$name]); |
||||
$registered[$name] = true; |
||||
} elseif ($registered[$name] === false) { |
||||
throw new Exception("A circular dependency is detected for target '$name'."); |
||||
} |
||||
} |
||||
|
||||
protected function saveTargets($targets, $bundleFile) |
||||
{ |
||||
$array = array(); |
||||
foreach ($targets as $name => $target) { |
||||
foreach (array('js', 'css', 'depends', 'basePath', 'baseUrl') as $prop) { |
||||
if (!empty($target->$prop)) { |
||||
$array[$name][$prop] = $target->$prop; |
||||
} |
||||
} |
||||
} |
||||
$array = var_export($array, true); |
||||
$version = date('Y-m-d H:i:s', time()); |
||||
file_put_contents($bundleFile, <<<EOD |
||||
<?php |
||||
/** |
||||
* This file is generated by the "yiic script" command. |
||||
* DO NOT MODIFY THIS FILE DIRECTLY. |
||||
* @version $version |
||||
*/ |
||||
return $array; |
||||
EOD |
||||
); |
||||
} |
||||
|
||||
protected function compressJsFiles($inputFiles, $outputFile) |
||||
{ |
||||
if (is_string($this->jsCompressor)) { |
||||
$tmpFile = $outputFile . '.tmp'; |
||||
$this->combineJsFiles($inputFiles, $tmpFile); |
||||
$log = shell_exec(strtr($this->jsCompressor, array( |
||||
'{from}' => $tmpFile, |
||||
'{to}' => $outputFile, |
||||
))); |
||||
@unlink($tmpFile); |
||||
} else { |
||||
$log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile); |
||||
} |
||||
} |
||||
|
||||
protected function compressCssFiles($inputFiles, $outputFile) |
||||
{ |
||||
if (is_string($this->cssCompressor)) { |
||||
$tmpFile = $outputFile . '.tmp'; |
||||
$this->combineCssFiles($inputFiles, $tmpFile); |
||||
$log = shell_exec(strtr($this->cssCompressor, array( |
||||
'{from}' => $inputFiles, |
||||
'{to}' => $outputFile, |
||||
))); |
||||
} else { |
||||
$log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); |
||||
} |
||||
} |
||||
|
||||
public function combineJsFiles($files, $tmpFile) |
||||
{ |
||||
$content = ''; |
||||
foreach ($files as $file) { |
||||
$content .= "/*** BEGIN FILE: $file ***/\n" |
||||
. file_get_contents($file) |
||||
. "/*** END FILE: $file ***/\n"; |
||||
} |
||||
file_put_contents($tmpFile, $content); |
||||
} |
||||
|
||||
public function combineCssFiles($files, $tmpFile) |
||||
{ |
||||
// todo: adjust url() references in CSS files |
||||
$content = ''; |
||||
foreach ($files as $file) { |
||||
$content .= "/*** BEGIN FILE: $file ***/\n" |
||||
. file_get_contents($file) |
||||
. "/*** END FILE: $file ***/\n"; |
||||
} |
||||
file_put_contents($tmpFile, $content); |
||||
} |
||||
|
||||
public function actionTemplate($configFile) |
||||
{ |
||||
$template = <<<EOD |
||||
<?php |
||||
|
||||
return array( |
||||
// |
||||
'bundles' => require('path/to/bundles.php'), |
||||
// |
||||
'extensions' => require('path/to/namespaces.php'), |
||||
// |
||||
'targets' => array( |
||||
'all' => array( |
||||
'basePath' => __DIR__, |
||||
'baseUrl' => '/test', |
||||
'js' => 'all-{ts}.js', |
||||
'css' => 'all-{ts}.css', |
||||
), |
||||
), |
||||
|
||||
'assetManager' => array( |
||||
'basePath' => __DIR__, |
||||
'baseUrl' => '/test', |
||||
), |
||||
); |
||||
EOD; |
||||
file_put_contents($configFile, $template); |
||||
} |
||||
} |
@ -1,10 +1,10 @@
|
||||
<?php |
||||
define('YII_DEBUG', true); |
||||
|
||||
$yii = __DIR__.'/../framework/yii.php'; |
||||
require $yii; |
||||
require __DIR__.'/../framework/yii.php'; |
||||
|
||||
$config = require dirname(__DIR__).'/protected/config/main.php'; |
||||
$config['basePath'] = dirname(__DIR__).'/protected'; |
||||
|
||||
$basePath = dirname(__DIR__).'/protected'; |
||||
$app = new \yii\web\Application('webapp', $basePath, $config); |
||||
$app = new \yii\web\Application($config); |
||||
$app->run(); |
@ -0,0 +1,340 @@
|
||||
<?php |
||||
/** |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @link http://www.yiiframework.com/ |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
use Yii; |
||||
use yii\base\InvalidParamException; |
||||
|
||||
/** |
||||
* ArrayHelper provides additional array functionality you can use in your |
||||
* application. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class ArrayHelper |
||||
{ |
||||
/** |
||||
* Merges two or more arrays into one recursively. |
||||
* If each array has an element with the same string key value, the latter |
||||
* will overwrite the former (different from array_merge_recursive). |
||||
* Recursive merging will be conducted if both arrays have an element of array |
||||
* type and are having the same key. |
||||
* For integer-keyed elements, the elements from the latter array will |
||||
* be appended to the former array. |
||||
* @param array $a array to be merged to |
||||
* @param array $b array to be merged from. You can specify additional |
||||
* arrays via third argument, fourth argument etc. |
||||
* @return array the merged array (the original arrays are not changed.) |
||||
*/ |
||||
public static function merge($a, $b) |
||||
{ |
||||
$args = func_get_args(); |
||||
$res = array_shift($args); |
||||
while ($args !== array()) { |
||||
$next = array_shift($args); |
||||
foreach ($next as $k => $v) { |
||||
if (is_integer($k)) { |
||||
isset($res[$k]) ? $res[] = $v : $res[$k] = $v; |
||||
} elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) { |
||||
$res[$k] = self::merge($res[$k], $v); |
||||
} else { |
||||
$res[$k] = $v; |
||||
} |
||||
} |
||||
} |
||||
return $res; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the value of an array element or object property with the given key or property name. |
||||
* If the key does not exist in the array, the default value will be returned instead. |
||||
* |
||||
* Below are some usage examples, |
||||
* |
||||
* ~~~ |
||||
* // working with array |
||||
* $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username'); |
||||
* // working with object |
||||
* $username = \yii\helpers\ArrayHelper::getValue($user, 'username'); |
||||
* // working with anonymous function |
||||
* $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) { |
||||
* return $user->firstName . ' ' . $user->lastName; |
||||
* }); |
||||
* ~~~ |
||||
* |
||||
* @param array|object $array array or object to extract value from |
||||
* @param string|\Closure $key key name of the array element, or property name of the object, |
||||
* or an anonymous function returning the value. The anonymous function signature should be: |
||||
* `function($array, $defaultValue)`. |
||||
* @param mixed $default the default value to be returned if the specified key does not exist |
||||
* @return mixed the value of the |
||||
*/ |
||||
public static function getValue($array, $key, $default = null) |
||||
{ |
||||
if ($key instanceof \Closure) { |
||||
return $key($array, $default); |
||||
} elseif (is_array($array)) { |
||||
return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default; |
||||
} else { |
||||
return $array->$key; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Indexes an array according to a specified key. |
||||
* The input array should be multidimensional or an array of objects. |
||||
* |
||||
* The key can be a key name of the sub-array, a property name of object, or an anonymous |
||||
* function which returns the key value given an array element. |
||||
* |
||||
* If a key value is null, the corresponding array element will be discarded and not put in the result. |
||||
* |
||||
* For example, |
||||
* |
||||
* ~~~ |
||||
* $array = array( |
||||
* array('id' => '123', 'data' => 'abc'), |
||||
* array('id' => '345', 'data' => 'def'), |
||||
* ); |
||||
* $result = ArrayHelper::index($array, 'id'); |
||||
* // the result is: |
||||
* // array( |
||||
* // '123' => array('id' => '123', 'data' => 'abc'), |
||||
* // '345' => array('id' => '345', 'data' => 'def'), |
||||
* // ) |
||||
* |
||||
* // using anonymous function |
||||
* $result = ArrayHelper::index($array, function(element) { |
||||
* return $element['id']; |
||||
* }); |
||||
* ~~~ |
||||
* |
||||
* @param array $array the array that needs to be indexed |
||||
* @param string|\Closure $key the column name or anonymous function whose result will be used to index the array |
||||
* @return array the indexed array |
||||
*/ |
||||
public static function index($array, $key) |
||||
{ |
||||
$result = array(); |
||||
foreach ($array as $element) { |
||||
$value = static::getValue($element, $key); |
||||
$result[$value] = $element; |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the values of a specified column in an array. |
||||
* The input array should be multidimensional or an array of objects. |
||||
* |
||||
* For example, |
||||
* |
||||
* ~~~ |
||||
* $array = array( |
||||
* array('id' => '123', 'data' => 'abc'), |
||||
* array('id' => '345', 'data' => 'def'), |
||||
* ); |
||||
* $result = ArrayHelper::getColumn($array, 'id'); |
||||
* // the result is: array( '123', '345') |
||||
* |
||||
* // using anonymous function |
||||
* $result = ArrayHelper::getColumn($array, function(element) { |
||||
* return $element['id']; |
||||
* }); |
||||
* ~~~ |
||||
* |
||||
* @param array $array |
||||
* @param string|\Closure $name |
||||
* @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array |
||||
* will be re-indexed with integers. |
||||
* @return array the list of column values |
||||
*/ |
||||
public static function getColumn($array, $name, $keepKeys = true) |
||||
{ |
||||
$result = array(); |
||||
if ($keepKeys) { |
||||
foreach ($array as $k => $element) { |
||||
$result[$k] = static::getValue($element, $name); |
||||
} |
||||
} else { |
||||
foreach ($array as $element) { |
||||
$result[] = static::getValue($element, $name); |
||||
} |
||||
} |
||||
|
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Builds a map (key-value pairs) from a multidimensional array or an array of objects. |
||||
* The `$from` and `$to` parameters specify the key names or property names to set up the map. |
||||
* Optionally, one can further group the map according to a grouping field `$group`. |
||||
* |
||||
* For example, |
||||
* |
||||
* ~~~ |
||||
* $array = array( |
||||
* array('id' => '123', 'name' => 'aaa', 'class' => 'x'), |
||||
* array('id' => '124', 'name' => 'bbb', 'class' => 'x'), |
||||
* array('id' => '345', 'name' => 'ccc', 'class' => 'y'), |
||||
* ); |
||||
* |
||||
* $result = ArrayHelper::map($array, 'id', 'name'); |
||||
* // the result is: |
||||
* // array( |
||||
* // '123' => 'aaa', |
||||
* // '124' => 'bbb', |
||||
* // '345' => 'ccc', |
||||
* // ) |
||||
* |
||||
* $result = ArrayHelper::map($array, 'id', 'name', 'class'); |
||||
* // the result is: |
||||
* // array( |
||||
* // 'x' => array( |
||||
* // '123' => 'aaa', |
||||
* // '124' => 'bbb', |
||||
* // ), |
||||
* // 'y' => array( |
||||
* // '345' => 'ccc', |
||||
* // ), |
||||
* // ) |
||||
* ~~~ |
||||
* |
||||
* @param array $array |
||||
* @param string|\Closure $from |
||||
* @param string|\Closure $to |
||||
* @param string|\Closure $group |
||||
* @return array |
||||
*/ |
||||
public static function map($array, $from, $to, $group = null) |
||||
{ |
||||
$result = array(); |
||||
foreach ($array as $element) { |
||||
$key = static::getValue($element, $from); |
||||
$value = static::getValue($element, $to); |
||||
if ($group !== null) { |
||||
$result[static::getValue($element, $group)][$key] = $value; |
||||
} else { |
||||
$result[$key] = $value; |
||||
} |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Sorts an array of objects or arrays (with the same structure) by one or several keys. |
||||
* @param array $array the array to be sorted. The array will be modified after calling this method. |
||||
* @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array |
||||
* elements, a property name of the objects, or an anonymous function returning the values for comparison |
||||
* purpose. The anonymous function signature should be: `function($item)`. |
||||
* To sort by multiple keys, provide an array of keys here. |
||||
* @param boolean|array $ascending whether to sort in ascending or descending order. When |
||||
* sorting by multiple keys with different ascending orders, use an array of ascending flags. |
||||
* @param integer|array $sortFlag the PHP sort flag. Valid values include: |
||||
* `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, and `SORT_STRING | SORT_FLAG_CASE`. The last |
||||
* value is for sorting strings in case-insensitive manner. Please refer to |
||||
* See [PHP manual](http://php.net/manual/en/function.sort.php) for more details. |
||||
* When sorting by multiple keys with different sort flags, use an array of sort flags. |
||||
* @throws InvalidParamException if the $ascending or $sortFlag parameters do not have |
||||
* correct number of elements as that of $key. |
||||
*/ |
||||
public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR) |
||||
{ |
||||
$keys = is_array($key) ? $key : array($key); |
||||
if (empty($keys) || empty($array)) { |
||||
return; |
||||
} |
||||
$n = count($keys); |
||||
if (is_scalar($ascending)) { |
||||
$ascending = array_fill(0, $n, $ascending); |
||||
} elseif (count($ascending) !== $n) { |
||||
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||
} |
||||
if (is_scalar($sortFlag)) { |
||||
$sortFlag = array_fill(0, $n, $sortFlag); |
||||
} elseif (count($sortFlag) !== $n) { |
||||
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||
} |
||||
$args = array(); |
||||
foreach ($keys as $i => $key) { |
||||
$flag = $sortFlag[$i]; |
||||
if ($flag == (SORT_STRING | SORT_FLAG_CASE)) { |
||||
$flag = SORT_STRING; |
||||
$column = array(); |
||||
foreach (static::getColumn($array, $key) as $k => $value) { |
||||
$column[$k] = strtolower($value); |
||||
} |
||||
$args[] = $column; |
||||
} else { |
||||
$args[] = static::getColumn($array, $key); |
||||
} |
||||
$args[] = $ascending[$i] ? SORT_ASC : SORT_DESC; |
||||
$args[] = $flag; |
||||
} |
||||
$args[] = &$array; |
||||
call_user_func_array('array_multisort', $args); |
||||
} |
||||
|
||||
/** |
||||
* Encodes special characters in an array of strings into HTML entities. |
||||
* Both the array keys and values will be encoded. |
||||
* If a value is an array, this method will also encode it recursively. |
||||
* @param array $data data to be encoded |
||||
* @param boolean $valuesOnly whether to encode array values only. If false, |
||||
* both the array keys and array values will be encoded. |
||||
* @param string $charset the charset that the data is using. If not set, |
||||
* [[\yii\base\Application::charset]] will be used. |
||||
* @return array the encoded data |
||||
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||
*/ |
||||
public static function htmlEncode($data, $valuesOnly = true, $charset = null) |
||||
{ |
||||
if ($charset === null) { |
||||
$charset = Yii::$app->charset; |
||||
} |
||||
$d = array(); |
||||
foreach ($data as $key => $value) { |
||||
if (!$valuesOnly && is_string($key)) { |
||||
$key = htmlspecialchars($key, ENT_QUOTES, $charset); |
||||
} |
||||
if (is_string($value)) { |
||||
$d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset); |
||||
} elseif (is_array($value)) { |
||||
$d[$key] = static::htmlEncode($value, $charset); |
||||
} |
||||
} |
||||
return $d; |
||||
} |
||||
|
||||
/** |
||||
* Decodes HTML entities into the corresponding characters in an array of strings. |
||||
* Both the array keys and values will be decoded. |
||||
* If a value is an array, this method will also decode it recursively. |
||||
* @param array $data data to be decoded |
||||
* @param boolean $valuesOnly whether to decode array values only. If false, |
||||
* both the array keys and array values will be decoded. |
||||
* @return array the decoded data |
||||
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||
*/ |
||||
public static function htmlDecode($data, $valuesOnly = true) |
||||
{ |
||||
$d = array(); |
||||
foreach ($data as $key => $value) { |
||||
if (!$valuesOnly && is_string($key)) { |
||||
$key = htmlspecialchars_decode($key, ENT_QUOTES); |
||||
} |
||||
if (is_string($value)) { |
||||
$d[$key] = htmlspecialchars_decode($value, ENT_QUOTES); |
||||
} elseif (is_array($value)) { |
||||
$d[$key] = static::htmlDecode($value); |
||||
} |
||||
} |
||||
return $d; |
||||
} |
||||
} |
@ -0,0 +1,470 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
// todo test this on all kinds of terminals, especially windows (check out lib ncurses) |
||||
|
||||
/** |
||||
* Console View is the base class for console view components |
||||
* |
||||
* A console view provides functionality to create rich console application by allowing to format output |
||||
* by adding color and font style to it. |
||||
* |
||||
* @author Carsten Brandt <mail@cebe.cc> |
||||
* @since 2.0 |
||||
*/ |
||||
class ConsoleColor |
||||
{ |
||||
const FG_BLACK = 30; |
||||
const FG_RED = 31; |
||||
const FG_GREEN = 32; |
||||
const FG_YELLOW = 33; |
||||
const FG_BLUE = 34; |
||||
const FG_PURPLE = 35; |
||||
const FG_CYAN = 36; |
||||
const FG_GREY = 37; |
||||
|
||||
const BG_BLACK = 40; |
||||
const BG_RED = 41; |
||||
const BG_GREEN = 42; |
||||
const BG_YELLOW = 43; |
||||
const BG_BLUE = 44; |
||||
const BG_PURPLE = 45; |
||||
const BG_CYAN = 46; |
||||
const BG_GREY = 47; |
||||
|
||||
const BOLD = 1; |
||||
const ITALIC = 3; |
||||
const UNDERLINE = 4; |
||||
const BLINK = 5; |
||||
const NEGATIVE = 7; |
||||
const CONCEALED = 8; |
||||
const CROSSED_OUT = 9; |
||||
const FRAMED = 51; |
||||
const ENCIRCLED = 52; |
||||
const OVERLINED = 53; |
||||
|
||||
/** |
||||
* Moves the terminal cursor up by sending ANSI control code CUU to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $rows number of rows the cursor should be moved up |
||||
*/ |
||||
public static function moveCursorUp($rows=1) |
||||
{ |
||||
echo "\033[" . (int) $rows . 'A'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor down by sending ANSI control code CUD to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $rows number of rows the cursor should be moved down |
||||
*/ |
||||
public static function moveCursorDown($rows=1) |
||||
{ |
||||
echo "\033[" . (int) $rows . 'B'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor forward by sending ANSI control code CUF to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $steps number of steps the cursor should be moved forward |
||||
*/ |
||||
public static function moveCursorForward($steps=1) |
||||
{ |
||||
echo "\033[" . (int) $steps . 'C'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor backward by sending ANSI control code CUB to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $steps number of steps the cursor should be moved backward |
||||
*/ |
||||
public static function moveCursorBackward($steps=1) |
||||
{ |
||||
echo "\033[" . (int) $steps . 'D'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal. |
||||
* @param integer $lines number of lines the cursor should be moved down |
||||
*/ |
||||
public static function moveCursorNextLine($lines=1) |
||||
{ |
||||
echo "\033[" . (int) $lines . 'E'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal. |
||||
* @param integer $lines number of lines the cursor should be moved up |
||||
*/ |
||||
public static function moveCursorPrevLine($lines=1) |
||||
{ |
||||
echo "\033[" . (int) $lines . 'F'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal. |
||||
* @param integer $column 1-based column number, 1 is the left edge of the screen. |
||||
* @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line. |
||||
*/ |
||||
public static function moveCursorTo($column, $row=null) |
||||
{ |
||||
if ($row === null) { |
||||
echo "\033[" . (int) $column . 'G'; |
||||
} else { |
||||
echo "\033[" . (int) $row . ';' . (int) $column . 'H'; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Scrolls whole page up by sending ANSI control code SU to the terminal. |
||||
* New lines are added at the bottom. This is not supported by ANSI.SYS used in windows. |
||||
* @param int $lines number of lines to scroll up |
||||
*/ |
||||
public static function scrollUp($lines=1) |
||||
{ |
||||
echo "\033[".(int)$lines."S"; |
||||
} |
||||
|
||||
/** |
||||
* Scrolls whole page down by sending ANSI control code SD to the terminal. |
||||
* New lines are added at the top. This is not supported by ANSI.SYS used in windows. |
||||
* @param int $lines number of lines to scroll down |
||||
*/ |
||||
public static function scrollDown($lines=1) |
||||
{ |
||||
echo "\033[".(int)$lines."T"; |
||||
} |
||||
|
||||
/** |
||||
* Saves the current cursor position by sending ANSI control code SCP to the terminal. |
||||
* Position can then be restored with {@link restoreCursorPosition}. |
||||
*/ |
||||
public static function saveCursorPosition() |
||||
{ |
||||
echo "\033[s"; |
||||
} |
||||
|
||||
/** |
||||
* Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal. |
||||
*/ |
||||
public static function restoreCursorPosition() |
||||
{ |
||||
echo "\033[u"; |
||||
} |
||||
|
||||
/** |
||||
* Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal. |
||||
* Use {@link showCursor} to bring it back. |
||||
* Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit. |
||||
*/ |
||||
public static function hideCursor() |
||||
{ |
||||
echo "\033[?25l"; |
||||
} |
||||
|
||||
/** |
||||
* Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal. |
||||
*/ |
||||
public static function showCursor() |
||||
{ |
||||
echo "\033[?25h"; |
||||
} |
||||
|
||||
/** |
||||
* Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal. |
||||
* Cursor position will not be changed. |
||||
* **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen. |
||||
*/ |
||||
public static function clearScreen() |
||||
{ |
||||
echo "\033[2J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearScreenBeforeCursor() |
||||
{ |
||||
echo "\033[1J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearScreenAfterCursor() |
||||
{ |
||||
echo "\033[0J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLine() |
||||
{ |
||||
echo "\033[2K"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLineBeforeCursor() |
||||
{ |
||||
echo "\033[1K"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLineAfterCursor() |
||||
{ |
||||
echo "\033[0K"; |
||||
} |
||||
|
||||
/** |
||||
* Will send ANSI format for following output |
||||
* |
||||
* You can pass any of the FG_*, BG_* and TEXT_* constants and also xterm256ColorBg |
||||
* TODO: documentation |
||||
*/ |
||||
public static function ansiStyle() |
||||
{ |
||||
echo "\033[" . implode(';', func_get_args()) . 'm'; |
||||
} |
||||
|
||||
/** |
||||
* Will return a string formatted with the given ANSI style |
||||
* |
||||
* See {@link ansiStyle} for possible arguments. |
||||
* @param string $string the string to be formatted |
||||
* @return string |
||||
*/ |
||||
public static function ansiStyleString($string) |
||||
{ |
||||
$args = func_get_args(); |
||||
array_shift($args); |
||||
$code = implode(';', $args); |
||||
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string."\033[0m"; |
||||
} |
||||
|
||||
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||||
public static function xterm256ColorFg($i) // TODO naming! |
||||
{ |
||||
return '38;5;'.$i; |
||||
} |
||||
|
||||
public static function xterm256ColorBg($i) // TODO naming! |
||||
{ |
||||
return '48;5;'.$i; |
||||
} |
||||
|
||||
/** |
||||
* Usage: list($w, $h) = ConsoleHelper::getScreenSize(); |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function getScreenSize() |
||||
{ |
||||
// TODO implement |
||||
return array(150,50); |
||||
} |
||||
|
||||
/** |
||||
* resets any ansi style set by previous method {@link ansiStyle} |
||||
* Any output after this is will have default text style. |
||||
*/ |
||||
public static function reset() |
||||
{ |
||||
echo "\033[0m"; |
||||
} |
||||
|
||||
/** |
||||
* Strips ANSI control codes from a string |
||||
* |
||||
* @param string $string String to strip |
||||
* @return string |
||||
*/ |
||||
public static function strip($string) |
||||
{ |
||||
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color |
||||
} |
||||
|
||||
// TODO refactor and review |
||||
public static function ansiToHtml($string) |
||||
{ |
||||
$tags = 0; |
||||
return preg_replace_callback('/\033\[[\d;]+m/', function($ansi) use (&$tags) { |
||||
$styleA = array(); |
||||
foreach(explode(';', $ansi) as $controlCode) |
||||
{ |
||||
switch($controlCode) |
||||
{ |
||||
case static::FG_BLACK: $style = array('color' => '#000000'); break; |
||||
case static::FG_BLUE: $style = array('color' => '#000078'); break; |
||||
case static::FG_CYAN: $style = array('color' => '#007878'); break; |
||||
case static::FG_GREEN: $style = array('color' => '#007800'); break; |
||||
case static::FG_GREY: $style = array('color' => '#787878'); break; |
||||
case static::FG_PURPLE: $style = array('color' => '#780078'); break; |
||||
case static::FG_RED: $style = array('color' => '#780000'); break; |
||||
case static::FG_YELLOW: $style = array('color' => '#787800'); break; |
||||
case static::BG_BLACK: $style = array('background-color' => '#000000'); break; |
||||
case static::BG_BLUE: $style = array('background-color' => '#000078'); break; |
||||
case static::BG_CYAN: $style = array('background-color' => '#007878'); break; |
||||
case static::BG_GREEN: $style = array('background-color' => '#007800'); break; |
||||
case static::BG_GREY: $style = array('background-color' => '#787878'); break; |
||||
case static::BG_PURPLE: $style = array('background-color' => '#780078'); break; |
||||
case static::BG_RED: $style = array('background-color' => '#780000'); break; |
||||
case static::BG_YELLOW: $style = array('background-color' => '#787800'); break; |
||||
case static::BOLD: $style = array('font-weight' => 'bold'); break; |
||||
case static::ITALIC: $style = array('font-style' => 'italic'); break; |
||||
case static::UNDERLINE: $style = array('text-decoration' => array('underline')); break; |
||||
case static::OVERLINED: $style = array('text-decoration' => array('overline')); break; |
||||
case static::CROSSED_OUT:$style = array('text-decoration' => array('line-through')); break; |
||||
case static::BLINK: $style = array('text-decoration' => array('blink')); break; |
||||
case static::NEGATIVE: // ??? |
||||
case static::CONCEALED: |
||||
case static::ENCIRCLED: |
||||
case static::FRAMED: |
||||
// TODO allow resetting codes |
||||
break; |
||||
case 0: // ansi reset |
||||
$return = ''; |
||||
for($n=$tags; $tags>0; $tags--) { |
||||
$return .= '</span>'; |
||||
} |
||||
return $return; |
||||
} |
||||
|
||||
$styleA = ArrayHelper::merge($styleA, $style); |
||||
} |
||||
$styleString[] = array(); |
||||
foreach($styleA as $name => $content) { |
||||
if ($name === 'text-decoration') { |
||||
$content = implode(' ', $content); |
||||
} |
||||
$styleString[] = $name.':'.$content; |
||||
} |
||||
$tags++; |
||||
return '<span' . (!empty($styleString) ? 'style="' . implode(';', $styleString) : '') . '>'; |
||||
}, $string); |
||||
} |
||||
|
||||
/** |
||||
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php |
||||
* |
||||
* Converts colorcodes in the format %y (for yellow) into ansi-control |
||||
* codes. The conversion table is: ('bold' meaning 'light' on some |
||||
* terminals). It's almost the same conversion table irssi uses. |
||||
* <pre> |
||||
* text text background |
||||
* ------------------------------------------------ |
||||
* %k %K %0 black dark grey black |
||||
* %r %R %1 red bold red red |
||||
* %g %G %2 green bold green green |
||||
* %y %Y %3 yellow bold yellow yellow |
||||
* %b %B %4 blue bold blue blue |
||||
* %m %M %5 magenta bold magenta magenta |
||||
* %p %P magenta (think: purple) |
||||
* %c %C %6 cyan bold cyan cyan |
||||
* %w %W %7 white bold white white |
||||
* |
||||
* %F Blinking, Flashing |
||||
* %U Underline |
||||
* %8 Reverse |
||||
* %_,%9 Bold |
||||
* |
||||
* %n Resets the color |
||||
* %% A single % |
||||
* </pre> |
||||
* First param is the string to convert, second is an optional flag if |
||||
* colors should be used. It defaults to true, if set to false, the |
||||
* colorcodes will just be removed (And %% will be transformed into %) |
||||
* |
||||
* @param string $string String to convert |
||||
* @param bool $colored Should the string be colored? |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function renderColoredString($string) |
||||
{ |
||||
$colored = true; |
||||
|
||||
|
||||
static $conversions = array ( // static so the array doesn't get built |
||||
// everytime |
||||
// %y - yellow, and so on... {{{ |
||||
'%y' => array('color' => 'yellow'), |
||||
'%g' => array('color' => 'green' ), |
||||
'%b' => array('color' => 'blue' ), |
||||
'%r' => array('color' => 'red' ), |
||||
'%p' => array('color' => 'purple'), |
||||
'%m' => array('color' => 'purple'), |
||||
'%c' => array('color' => 'cyan' ), |
||||
'%w' => array('color' => 'grey' ), |
||||
'%k' => array('color' => 'black' ), |
||||
'%n' => array('color' => 'reset' ), |
||||
'%Y' => array('color' => 'yellow', 'style' => 'light'), |
||||
'%G' => array('color' => 'green', 'style' => 'light'), |
||||
'%B' => array('color' => 'blue', 'style' => 'light'), |
||||
'%R' => array('color' => 'red', 'style' => 'light'), |
||||
'%P' => array('color' => 'purple', 'style' => 'light'), |
||||
'%M' => array('color' => 'purple', 'style' => 'light'), |
||||
'%C' => array('color' => 'cyan', 'style' => 'light'), |
||||
'%W' => array('color' => 'grey', 'style' => 'light'), |
||||
'%K' => array('color' => 'black', 'style' => 'light'), |
||||
'%N' => array('color' => 'reset', 'style' => 'light'), |
||||
'%3' => array('background' => 'yellow'), |
||||
'%2' => array('background' => 'green' ), |
||||
'%4' => array('background' => 'blue' ), |
||||
'%1' => array('background' => 'red' ), |
||||
'%5' => array('background' => 'purple'), |
||||
'%6' => array('background' => 'cyan' ), |
||||
'%7' => array('background' => 'grey' ), |
||||
'%0' => array('background' => 'black' ), |
||||
// Don't use this, I can't stand flashing text |
||||
'%F' => array('style' => 'blink'), |
||||
'%U' => array('style' => 'underline'), |
||||
'%8' => array('style' => 'inverse'), |
||||
'%9' => array('style' => 'bold'), |
||||
'%_' => array('style' => 'bold') |
||||
// }}} |
||||
); |
||||
|
||||
if ($colored) { |
||||
$string = str_replace('%%', '% ', $string); |
||||
foreach ($conversions as $key => $value) { |
||||
$string = str_replace($key, Console_Color::color($value), |
||||
$string); |
||||
} |
||||
$string = str_replace('% ', '%', $string); |
||||
|
||||
} else { |
||||
$string = preg_replace('/%((%)|.)/', '$2', $string); |
||||
} |
||||
|
||||
return $string; |
||||
} |
||||
|
||||
/** |
||||
* Escapes % so they don't get interpreted as color codes |
||||
* |
||||
* @param string $string String to escape |
||||
* |
||||
* @access public |
||||
* @return string |
||||
*/ |
||||
public static function escape($string) |
||||
{ |
||||
return str_replace('%', '%%', $string); |
||||
} |
||||
} |
@ -0,0 +1,172 @@
|
||||
<?php |
||||
/** |
||||
* Filesystem helper class file. |
||||
* |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
use Yii; |
||||
|
||||
/** |
||||
* Filesystem helper |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @author Alex Makarov <sam@rmcreative.ru> |
||||
* @since 2.0 |
||||
*/ |
||||
class FileHelper |
||||
{ |
||||
/** |
||||
* 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 ? self::getMimeTypeByExtension($file) : null; |
||||
} |
||||
|
||||
/** |
||||
* Determines the MIME type based on the extension name of the specified file. |
||||
* This method will use a local map between extension names and MIME types. |
||||
* @param string $file the file name. |
||||
* @param string $magicFile the path of the file that contains all available MIME type information. |
||||
* If this is not set, the default file aliased by `@yii/util/mimeTypes.php` will be used. |
||||
* @return string the MIME type. Null is returned if the MIME type cannot be determined. |
||||
*/ |
||||
public static function getMimeTypeByExtension($file, $magicFile = null) |
||||
{ |
||||
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 0777. |
||||
* - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. |
||||
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file. |
||||
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled. |
||||
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or |
||||
* file to be copied from, while `$to` is the copy target. |
||||
* - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied. |
||||
* The signature of the callback is similar to that of `beforeCopy`. |
||||
*/ |
||||
public static function copyDirectory($src, $dst, $options = array()) |
||||
{ |
||||
if (!is_dir($dst)) { |
||||
mkdir($dst, isset($options['dirMode']) ? $options['dirMode'] : 0777, true); |
||||
} |
||||
|
||||
$handle = opendir($src); |
||||
while (($file = readdir($handle)) !== false) { |
||||
if ($file === '.' || $file === '..') { |
||||
continue; |
||||
} |
||||
$from = $src . DIRECTORY_SEPARATOR . $file; |
||||
$to = $dst . DIRECTORY_SEPARATOR . $file; |
||||
if (!isset($options['beforeCopy']) || call_user_func($options['beforeCopy'], $from, $to)) { |
||||
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); |
||||
} |
||||
} |
@ -0,0 +1,981 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
use Yii; |
||||
use yii\base\InvalidParamException; |
||||
|
||||
/** |
||||
* Html provides a set of static methods for generating commonly used HTML tags. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class Html |
||||
{ |
||||
/** |
||||
* @var boolean whether to close void (empty) elements. Defaults to true. |
||||
* @see voidElements |
||||
*/ |
||||
public static $closeVoidElements = true; |
||||
/** |
||||
* @var array list of void elements (element name => 1) |
||||
* @see closeVoidElements |
||||
* @see http://www.w3.org/TR/html-markup/syntax.html#void-element |
||||
*/ |
||||
public static $voidElements = array( |
||||
'area' => 1, |
||||
'base' => 1, |
||||
'br' => 1, |
||||
'col' => 1, |
||||
'command' => 1, |
||||
'embed' => 1, |
||||
'hr' => 1, |
||||
'img' => 1, |
||||
'input' => 1, |
||||
'keygen' => 1, |
||||
'link' => 1, |
||||
'meta' => 1, |
||||
'param' => 1, |
||||
'source' => 1, |
||||
'track' => 1, |
||||
'wbr' => 1, |
||||
); |
||||
/** |
||||
* @var boolean whether to show the values of boolean attributes in element tags. |
||||
* If false, only the attribute names will be generated. |
||||
* @see booleanAttributes |
||||
*/ |
||||
public static $showBooleanAttributeValues = true; |
||||
/** |
||||
* @var array list of boolean attributes. The presence of a boolean attribute on |
||||
* an element represents the true value, and the absence of the attribute represents the false value. |
||||
* @see showBooleanAttributeValues |
||||
* @see http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes |
||||
*/ |
||||
public static $booleanAttributes = array( |
||||
'async' => 1, |
||||
'autofocus' => 1, |
||||
'autoplay' => 1, |
||||
'checked' => 1, |
||||
'controls' => 1, |
||||
'declare' => 1, |
||||
'default' => 1, |
||||
'defer' => 1, |
||||
'disabled' => 1, |
||||
'formnovalidate' => 1, |
||||
'hidden' => 1, |
||||
'ismap' => 1, |
||||
'loop' => 1, |
||||
'multiple' => 1, |
||||
'muted' => 1, |
||||
'nohref' => 1, |
||||
'noresize' => 1, |
||||
'novalidate' => 1, |
||||
'open' => 1, |
||||
'readonly' => 1, |
||||
'required' => 1, |
||||
'reversed' => 1, |
||||
'scoped' => 1, |
||||
'seamless' => 1, |
||||
'selected' => 1, |
||||
'typemustmatch' => 1, |
||||
); |
||||
/** |
||||
* @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes |
||||
* that are rendered by [[renderAttributes()]]. |
||||
*/ |
||||
public static $attributeOrder = array( |
||||
'type', |
||||
'id', |
||||
'class', |
||||
'name', |
||||
'value', |
||||
|
||||
'href', |
||||
'src', |
||||
'action', |
||||
'method', |
||||
|
||||
'selected', |
||||
'checked', |
||||
'readonly', |
||||
'disabled', |
||||
'multiple', |
||||
|
||||
'size', |
||||
'maxlength', |
||||
'width', |
||||
'height', |
||||
'rows', |
||||
'cols', |
||||
|
||||
'alt', |
||||
'title', |
||||
'rel', |
||||
'media', |
||||
); |
||||
|
||||
/** |
||||
* Encodes special characters into HTML entities. |
||||
* The [[yii\base\Application::charset|application charset]] will be used for encoding. |
||||
* @param string $content the content to be encoded |
||||
* @return string the encoded content |
||||
* @see decode |
||||
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||
*/ |
||||
public static function encode($content) |
||||
{ |
||||
return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset); |
||||
} |
||||
|
||||
/** |
||||
* Decodes special HTML entities back to the corresponding characters. |
||||
* This is the opposite of [[encode()]]. |
||||
* @param string $content the content to be decoded |
||||
* @return string the decoded content |
||||
* @see encode |
||||
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||
*/ |
||||
public static function decode($content) |
||||
{ |
||||
return htmlspecialchars_decode($content, ENT_QUOTES); |
||||
} |
||||
|
||||
/** |
||||
* Generates a complete HTML tag. |
||||
* @param string $name the tag name |
||||
* @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded. |
||||
* If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated HTML tag |
||||
* @see beginTag |
||||
* @see endTag |
||||
*/ |
||||
public static function tag($name, $content = '', $options = array()) |
||||
{ |
||||
$html = '<' . $name . static::renderTagAttributes($options); |
||||
if (isset(static::$voidElements[strtolower($name)])) { |
||||
return $html . (static::$closeVoidElements ? ' />' : '>'); |
||||
} else { |
||||
return $html . ">$content</$name>"; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Generates a start tag. |
||||
* @param string $name the tag name |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated start tag |
||||
* @see endTag |
||||
* @see tag |
||||
*/ |
||||
public static function beginTag($name, $options = array()) |
||||
{ |
||||
return '<' . $name . static::renderTagAttributes($options) . '>'; |
||||
} |
||||
|
||||
/** |
||||
* Generates an end tag. |
||||
* @param string $name the tag name |
||||
* @return string the generated end tag |
||||
* @see beginTag |
||||
* @see tag |
||||
*/ |
||||
public static function endTag($name) |
||||
{ |
||||
return "</$name>"; |
||||
} |
||||
|
||||
/** |
||||
* Encloses the given content within a CDATA tag. |
||||
* @param string $content the content to be enclosed within the CDATA tag |
||||
* @return string the CDATA tag with the enclosed content. |
||||
*/ |
||||
public static function cdata($content) |
||||
{ |
||||
return '<![CDATA[' . $content . ']]>'; |
||||
} |
||||
|
||||
/** |
||||
* Generates a style tag. |
||||
* @param string $content the style content |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* If the options does not contain "type", a "type" attribute with value "text/css" will be used. |
||||
* @return string the generated style tag |
||||
*/ |
||||
public static function style($content, $options = array()) |
||||
{ |
||||
if (!isset($options['type'])) { |
||||
$options['type'] = 'text/css'; |
||||
} |
||||
return static::tag('style', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a script tag. |
||||
* @param string $content the script content |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered. |
||||
* @return string the generated script tag |
||||
*/ |
||||
public static function script($content, $options = array()) |
||||
{ |
||||
if (!isset($options['type'])) { |
||||
$options['type'] = 'text/javascript'; |
||||
} |
||||
return static::tag('script', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a link tag that refers to an external CSS file. |
||||
* @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]]. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated link tag |
||||
* @see url |
||||
*/ |
||||
public static function cssFile($url, $options = array()) |
||||
{ |
||||
$options['rel'] = 'stylesheet'; |
||||
$options['type'] = 'text/css'; |
||||
$options['href'] = static::url($url); |
||||
return static::tag('link', '', $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a script tag that refers to an external JavaScript file. |
||||
* @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]]. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated script tag |
||||
* @see url |
||||
*/ |
||||
public static function jsFile($url, $options = array()) |
||||
{ |
||||
$options['type'] = 'text/javascript'; |
||||
$options['src'] = static::url($url); |
||||
return static::tag('script', '', $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a form start tag. |
||||
* @param array|string $action the form action URL. This parameter will be processed by [[url()]]. |
||||
* @param string $method the form submission method, either "post" or "get" (case-insensitive) |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated form start tag. |
||||
* @see endForm |
||||
*/ |
||||
public static function beginForm($action = '', $method = 'post', $options = array()) |
||||
{ |
||||
$action = static::url($action); |
||||
|
||||
// query parameters in the action are ignored for GET method |
||||
// we use hidden fields to add them back |
||||
$hiddens = array(); |
||||
if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { |
||||
foreach (explode('&', substr($action, $pos + 1)) as $pair) { |
||||
if (($pos1 = strpos($pair, '=')) !== false) { |
||||
$hiddens[] = static::hiddenInput(urldecode(substr($pair, 0, $pos1)), urldecode(substr($pair, $pos1 + 1))); |
||||
} else { |
||||
$hiddens[] = static::hiddenInput(urldecode($pair), ''); |
||||
} |
||||
} |
||||
$action = substr($action, 0, $pos); |
||||
} |
||||
|
||||
$options['action'] = $action; |
||||
$options['method'] = $method; |
||||
$form = static::beginTag('form', $options); |
||||
if ($hiddens !== array()) { |
||||
$form .= "\n" . implode("\n", $hiddens); |
||||
} |
||||
|
||||
return $form; |
||||
} |
||||
|
||||
/** |
||||
* Generates a form end tag. |
||||
* @return string the generated tag |
||||
* @see beginForm |
||||
*/ |
||||
public static function endForm() |
||||
{ |
||||
return '</form>'; |
||||
} |
||||
|
||||
/** |
||||
* Generates a hyperlink tag. |
||||
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||
* it to prevent XSS attacks. |
||||
* @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]] |
||||
* and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute |
||||
* will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated hyperlink |
||||
* @see url |
||||
*/ |
||||
public static function a($text, $url = null, $options = array()) |
||||
{ |
||||
if ($url !== null) { |
||||
$options['href'] = static::url($url); |
||||
} |
||||
return static::tag('a', $text, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a mailto hyperlink. |
||||
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||
* it to prevent XSS attacks. |
||||
* @param string $email email address. If this is null, the first parameter (link body) will be treated |
||||
* as the email address and used. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated mailto link |
||||
*/ |
||||
public static function mailto($text, $email = null, $options = array()) |
||||
{ |
||||
return static::a($text, 'mailto:' . ($email === null ? $text : $email), $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates an image tag. |
||||
* @param string $src the image URL. This parameter will be processed by [[url()]]. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated image tag |
||||
*/ |
||||
public static function img($src, $options = array()) |
||||
{ |
||||
$options['src'] = static::url($src); |
||||
if (!isset($options['alt'])) { |
||||
$options['alt'] = ''; |
||||
} |
||||
return static::tag('img', null, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a label tag. |
||||
* @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||
* it to prevent XSS attacks. |
||||
* @param string $for the ID of the HTML element that this label is associated with. |
||||
* If this is null, the "for" attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated label tag |
||||
*/ |
||||
public static function label($content, $for = null, $options = array()) |
||||
{ |
||||
$options['for'] = $for; |
||||
return static::tag('label', $content, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a button tag. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||
* you should consider [[encode()]] it to prevent XSS attacks. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* If the options does not contain "type", a "type" attribute with value "button" will be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function button($name = null, $value = null, $content = 'Button', $options = array()) |
||||
{ |
||||
$options['name'] = $name; |
||||
$options['value'] = $value; |
||||
if (!isset($options['type'])) { |
||||
$options['type'] = 'button'; |
||||
} |
||||
return static::tag('button', $content, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a submit button tag. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||
* you should consider [[encode()]] it to prevent XSS attacks. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated submit button tag |
||||
*/ |
||||
public static function submitButton($name = null, $value = null, $content = 'Submit', $options = array()) |
||||
{ |
||||
$options['type'] = 'submit'; |
||||
return static::button($name, $value, $content, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a reset button tag. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||
* you should consider [[encode()]] it to prevent XSS attacks. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated reset button tag |
||||
*/ |
||||
public static function resetButton($name = null, $value = null, $content = 'Reset', $options = array()) |
||||
{ |
||||
$options['type'] = 'reset'; |
||||
return static::button($name, $value, $content, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates an input type of the given type. |
||||
* @param string $type the type attribute. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated input tag |
||||
*/ |
||||
public static function input($type, $name = null, $value = null, $options = array()) |
||||
{ |
||||
$options['type'] = $type; |
||||
$options['name'] = $name; |
||||
$options['value'] = $value; |
||||
return static::tag('input', null, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates an input button. |
||||
* @param string $name the name attribute. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function buttonInput($name, $value = 'Button', $options = array()) |
||||
{ |
||||
return static::input('button', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a submit input button. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function submitInput($name = null, $value = 'Submit', $options = array()) |
||||
{ |
||||
return static::input('submit', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a reset input button. |
||||
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]]. |
||||
* Attributes whose value is null will be ignored and not put in the tag returned. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function resetInput($name = null, $value = 'Reset', $options = array()) |
||||
{ |
||||
return static::input('reset', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a text input field. |
||||
* @param string $name the name attribute. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function textInput($name, $value = null, $options = array()) |
||||
{ |
||||
return static::input('text', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a hidden input field. |
||||
* @param string $name the name attribute. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function hiddenInput($name, $value = null, $options = array()) |
||||
{ |
||||
return static::input('hidden', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a password input field. |
||||
* @param string $name the name attribute. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function passwordInput($name, $value = null, $options = array()) |
||||
{ |
||||
return static::input('password', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a file input field. |
||||
* To use a file input field, you should set the enclosing form's "enctype" attribute to |
||||
* be "multipart/form-data". After the form is submitted, the uploaded file information |
||||
* can be obtained via $_FILES[$name] (see PHP documentation). |
||||
* @param string $name the name attribute. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated button tag |
||||
*/ |
||||
public static function fileInput($name, $value = null, $options = array()) |
||||
{ |
||||
return static::input('file', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a text area input. |
||||
* @param string $name the input name |
||||
* @param string $value the input value. Note that it will be encoded using [[encode()]]. |
||||
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||
* If a value is null, the corresponding attribute will not be rendered. |
||||
* @return string the generated text area tag |
||||
*/ |
||||
public static function textarea($name, $value = '', $options = array()) |
||||
{ |
||||
$options['name'] = $name; |
||||
return static::tag('textarea', static::encode($value), $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a radio button input. |
||||
* @param string $name the name attribute. |
||||
* @param boolean $checked whether the radio button should be checked. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||
* |
||||
* - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute |
||||
* is present, a hidden input will be generated so that if the radio button is not checked and is submitted, |
||||
* the value of this attribute will still be submitted to the server via the hidden input. |
||||
* |
||||
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||
* |
||||
* @return string the generated radio button tag |
||||
*/ |
||||
public static function radio($name, $checked = false, $value = '1', $options = array()) |
||||
{ |
||||
$options['checked'] = $checked; |
||||
$options['value'] = $value; |
||||
if (isset($options['uncheck'])) { |
||||
// add a hidden field so that if the radio button is not selected, it still submits a value |
||||
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||
unset($options['uncheck']); |
||||
} else { |
||||
$hidden = ''; |
||||
} |
||||
return $hidden . static::input('radio', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a checkbox input. |
||||
* @param string $name the name attribute. |
||||
* @param boolean $checked whether the checkbox should be checked. |
||||
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||
* |
||||
* - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute |
||||
* is present, a hidden input will be generated so that if the checkbox is not checked and is submitted, |
||||
* the value of this attribute will still be submitted to the server via the hidden input. |
||||
* |
||||
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||
* |
||||
* @return string the generated checkbox tag |
||||
*/ |
||||
public static function checkbox($name, $checked = false, $value = '1', $options = array()) |
||||
{ |
||||
$options['checked'] = $checked; |
||||
$options['value'] = $value; |
||||
if (isset($options['uncheck'])) { |
||||
// add a hidden field so that if the checkbox is not selected, it still submits a value |
||||
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||
unset($options['uncheck']); |
||||
} else { |
||||
$hidden = ''; |
||||
} |
||||
return $hidden . static::input('checkbox', $name, $value, $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a drop-down list. |
||||
* @param string $name the input name |
||||
* @param string $selection the selected value |
||||
* @param array $items the option data items. The array keys are option values, and the array values |
||||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||
* If you have a list of data models, you may convert them into the format described above using |
||||
* [[\yii\helpers\ArrayHelper::map()]]. |
||||
* |
||||
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||
* the labels will also be HTML-encoded. |
||||
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||
* |
||||
* - prompt: string, a prompt text to be displayed as the first option; |
||||
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||
* |
||||
* ~~~ |
||||
* array( |
||||
* 'value1' => array('disabled' => true), |
||||
* 'value2' => array('label' => 'value 2'), |
||||
* ); |
||||
* ~~~ |
||||
* |
||||
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||
* except that the array keys represent the optgroup labels specified in $items. |
||||
* |
||||
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||
* |
||||
* @return string the generated drop-down list tag |
||||
*/ |
||||
public static function dropDownList($name, $selection = null, $items = array(), $options = array()) |
||||
{ |
||||
$options['name'] = $name; |
||||
$selectOptions = static::renderSelectOptions($selection, $items, $options); |
||||
return static::tag('select', "\n" . $selectOptions . "\n", $options); |
||||
} |
||||
|
||||
/** |
||||
* Generates a list box. |
||||
* @param string $name the input name |
||||
* @param string|array $selection the selected value(s) |
||||
* @param array $items the option data items. The array keys are option values, and the array values |
||||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||
* If you have a list of data models, you may convert them into the format described above using |
||||
* [[\yii\helpers\ArrayHelper::map()]]. |
||||
* |
||||
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||
* the labels will also be HTML-encoded. |
||||
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||
* |
||||
* - prompt: string, a prompt text to be displayed as the first option; |
||||
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||
* |
||||
* ~~~ |
||||
* array( |
||||
* 'value1' => array('disabled' => true), |
||||
* 'value2' => array('label' => 'value 2'), |
||||
* ); |
||||
* ~~~ |
||||
* |
||||
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||
* except that the array keys represent the optgroup labels specified in $items. |
||||
* - unselect: string, the value that will be submitted when no option is selected. |
||||
* When this attribute is set, a hidden field will be generated so that if no option is selected in multiple |
||||
* mode, we can still obtain the posted unselect value. |
||||
* |
||||
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||
* |
||||
* @return string the generated list box tag |
||||
*/ |
||||
public static function listBox($name, $selection = null, $items = array(), $options = array()) |
||||
{ |
||||
if (!isset($options['size'])) { |
||||
$options['size'] = 4; |
||||
} |
||||
if (!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. |
||||
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||
* @param array $options options (name => config) for the checkbox list. The following options are supported: |
||||
* |
||||
* - unselect: string, the value that should be submitted when none of the checkboxes is selected. |
||||
* By setting this option, a hidden input will be generated. |
||||
* - separator: string, the HTML code that separates items. |
||||
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||
* corresponding to a single item in $items. The signature of this callback must be: |
||||
* |
||||
* ~~~ |
||||
* function ($index, $label, $name, $checked, $value) |
||||
* ~~~ |
||||
* |
||||
* where $index is the zero-based index of the checkbox in the whole list; $label |
||||
* is the label for the checkbox; and $name, $value and $checked represent the name, |
||||
* value and the checked status of the checkbox input. |
||||
* @return string the generated checkbox list |
||||
*/ |
||||
public static function checkboxList($name, $selection = null, $items = array(), $options = array()) |
||||
{ |
||||
if (substr($name, -2) !== '[]') { |
||||
$name .= '[]'; |
||||
} |
||||
|
||||
$formatter = isset($options['item']) ? $options['item'] : null; |
||||
$lines = array(); |
||||
$index = 0; |
||||
foreach ($items as $value => $label) { |
||||
$checked = $selection !== null && |
||||
(!is_array($selection) && !strcmp($value, $selection) |
||||
|| is_array($selection) && in_array($value, $selection)); |
||||
if ($formatter !== null) { |
||||
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||
} else { |
||||
$lines[] = static::label(static::checkbox($name, $checked, $value) . ' ' . $label); |
||||
} |
||||
$index++; |
||||
} |
||||
|
||||
if (isset($options['unselect'])) { |
||||
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||
$name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name; |
||||
$hidden = static::hiddenInput($name2, $options['unselect']); |
||||
} else { |
||||
$hidden = ''; |
||||
} |
||||
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||
|
||||
return $hidden . implode($separator, $lines); |
||||
} |
||||
|
||||
/** |
||||
* Generates a list of radio buttons. |
||||
* A radio button list is like a checkbox list, except that it only allows single selection. |
||||
* @param string $name the name attribute of each radio button. |
||||
* @param string|array $selection the selected value(s). |
||||
* @param array $items the data item used to generate the radio buttons. |
||||
* The array keys are the labels, while the array values are the corresponding radio button values. |
||||
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||
* @param array $options options (name => config) for the radio button list. The following options are supported: |
||||
* |
||||
* - unselect: string, the value that should be submitted when none of the radio buttons is selected. |
||||
* By setting this option, a hidden input will be generated. |
||||
* - separator: string, the HTML code that separates items. |
||||
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||
* corresponding to a single item in $items. The signature of this callback must be: |
||||
* |
||||
* ~~~ |
||||
* function ($index, $label, $name, $checked, $value) |
||||
* ~~~ |
||||
* |
||||
* where $index is the zero-based index of the radio button in the whole list; $label |
||||
* is the label for the radio button; and $name, $value and $checked represent the name, |
||||
* value and the checked status of the radio button input. |
||||
* @return string the generated radio button list |
||||
*/ |
||||
public static function radioList($name, $selection = null, $items = array(), $options = array()) |
||||
{ |
||||
$formatter = isset($options['item']) ? $options['item'] : null; |
||||
$lines = array(); |
||||
$index = 0; |
||||
foreach ($items as $value => $label) { |
||||
$checked = $selection !== null && |
||||
(!is_array($selection) && !strcmp($value, $selection) |
||||
|| is_array($selection) && in_array($value, $selection)); |
||||
if ($formatter !== null) { |
||||
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||
} else { |
||||
$lines[] = static::label(static::radio($name, $checked, $value) . ' ' . $label); |
||||
} |
||||
$index++; |
||||
} |
||||
|
||||
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||
if (isset($options['unselect'])) { |
||||
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||
$hidden = static::hiddenInput($name, $options['unselect']); |
||||
} else { |
||||
$hidden = ''; |
||||
} |
||||
|
||||
return $hidden . implode($separator, $lines); |
||||
} |
||||
|
||||
/** |
||||
* Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]]. |
||||
* @param string|array $selection the selected value(s). This can be either a string for single selection |
||||
* or an array for multiple selections. |
||||
* @param array $items the option data items. The array keys are option values, and the array values |
||||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||
* If you have a list of data models, you may convert them into the format described above using |
||||
* [[\yii\helpers\ArrayHelper::map()]]. |
||||
* |
||||
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||
* the labels will also be HTML-encoded. |
||||
* @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call. |
||||
* This method will take out these elements, if any: "prompt", "options" and "groups". See more details |
||||
* in [[dropDownList()]] for the explanation of these elements. |
||||
* |
||||
* @return string the generated list options |
||||
*/ |
||||
public static function renderSelectOptions($selection, $items, &$tagOptions = array()) |
||||
{ |
||||
$lines = array(); |
||||
if (isset($tagOptions['prompt'])) { |
||||
$prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt'])); |
||||
$lines[] = static::tag('option', $prompt, array('value' => '')); |
||||
} |
||||
|
||||
$options = isset($tagOptions['options']) ? $tagOptions['options'] : array(); |
||||
$groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array(); |
||||
unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']); |
||||
|
||||
foreach ($items as $key => $value) { |
||||
if (is_array($value)) { |
||||
$groupAttrs = isset($groups[$key]) ? $groups[$key] : array(); |
||||
$groupAttrs['label'] = $key; |
||||
$attrs = array('options' => $options, 'groups' => $groups); |
||||
$content = static::renderSelectOptions($selection, $value, $attrs); |
||||
$lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs); |
||||
} else { |
||||
$attrs = isset($options[$key]) ? $options[$key] : array(); |
||||
$attrs['value'] = $key; |
||||
$attrs['selected'] = $selection !== null && |
||||
(!is_array($selection) && !strcmp($key, $selection) |
||||
|| is_array($selection) && in_array($key, $selection)); |
||||
$lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs); |
||||
} |
||||
} |
||||
|
||||
return implode("\n", $lines); |
||||
} |
||||
|
||||
/** |
||||
* Renders the HTML tag attributes. |
||||
* Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially |
||||
* according to [[booleanAttributes]] and [[showBooleanAttributeValues]]. |
||||
* @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. |
||||
* Attributes whose value is null will be ignored and not put in the rendering result. |
||||
* @return string the rendering result. If the attributes are not empty, they will be rendered |
||||
* into a string with a leading white space (such that it can be directly appended to the tag name |
||||
* in a tag. If there is no attribute, an empty string will be returned. |
||||
*/ |
||||
public static function renderTagAttributes($attributes) |
||||
{ |
||||
if (count($attributes) > 1) { |
||||
$sorted = array(); |
||||
foreach (static::$attributeOrder as $name) { |
||||
if (isset($attributes[$name])) { |
||||
$sorted[$name] = $attributes[$name]; |
||||
} |
||||
} |
||||
$attributes = array_merge($sorted, $attributes); |
||||
} |
||||
|
||||
$html = ''; |
||||
foreach ($attributes as $name => $value) { |
||||
if (isset(static::$booleanAttributes[strtolower($name)])) { |
||||
if ($value || strcasecmp($name, $value) === 0) { |
||||
$html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name"; |
||||
} |
||||
} elseif ($value !== null) { |
||||
$html .= " $name=\"" . static::encode($value) . '"'; |
||||
} |
||||
} |
||||
return $html; |
||||
} |
||||
|
||||
/** |
||||
* Normalizes the input parameter to be a valid URL. |
||||
* |
||||
* If the input parameter |
||||
* |
||||
* - is an empty string: the currently requested URL will be returned; |
||||
* - is a non-empty string: it will be processed by [[Yii::getAlias()]] and returned; |
||||
* - is an array: the first array element is considered a route, while the rest of the name-value |
||||
* pairs are treated as the parameters to be used for URL creation using [[\yii\web\Controller::createUrl()]]. |
||||
* For example: `array('post/index', 'page' => 2)`, `array('index')`. |
||||
* |
||||
* @param array|string $url the parameter to be used to generate a valid URL |
||||
* @return string the normalized URL |
||||
* @throws InvalidParamException if the parameter is invalid. |
||||
*/ |
||||
public static function url($url) |
||||
{ |
||||
if (is_array($url)) { |
||||
if (isset($url[0])) { |
||||
$route = $url[0]; |
||||
$params = array_splice($url, 1); |
||||
if (Yii::$app->controller !== null) { |
||||
return Yii::$app->controller->createUrl($route, $params); |
||||
} else { |
||||
return Yii::$app->getUrlManager()->createUrl($route, $params); |
||||
} |
||||
} else { |
||||
throw new InvalidParamException('The array specifying a URL must contain at least one element.'); |
||||
} |
||||
} elseif ($url === '') { |
||||
return Yii::$app->getRequest()->getUrl(); |
||||
} else { |
||||
return Yii::getAlias($url); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,272 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
use Yii; |
||||
use yii\base\Exception; |
||||
use yii\base\InvalidConfigException; |
||||
use yii\base\InvalidParamException; |
||||
|
||||
/** |
||||
* SecurityHelper provides a set of methods to handle common security-related tasks. |
||||
* |
||||
* In particular, SecurityHelper supports the following features: |
||||
* |
||||
* - Encryption/decryption: [[encrypt()]] and [[decrypt()]] |
||||
* - Data tampering prevention: [[hashData()]] and [[validateData()]] |
||||
* - Password validation: [[generatePasswordHash()]] and [[validatePassword()]] |
||||
* |
||||
* Additionally, SecurityHelper provides [[getSecretKey()]] to support generating |
||||
* named secret keys. These secret keys, once generated, will be stored in a file |
||||
* and made available in future requests. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @author Tom Worster <fsb@thefsb.org> |
||||
* @since 2.0 |
||||
*/ |
||||
class SecurityHelper |
||||
{ |
||||
/** |
||||
* Encrypts data. |
||||
* @param string $data data to be encrypted. |
||||
* @param string $key the encryption secret key |
||||
* @return string the encrypted data |
||||
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||
* @see decrypt() |
||||
*/ |
||||
public static function encrypt($data, $key) |
||||
{ |
||||
$module = static::openCryptModule(); |
||||
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||
srand(); |
||||
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); |
||||
mcrypt_generic_init($module, $key, $iv); |
||||
$encrypted = $iv . mcrypt_generic($module, $data); |
||||
mcrypt_generic_deinit($module); |
||||
mcrypt_module_close($module); |
||||
return $encrypted; |
||||
} |
||||
|
||||
/** |
||||
* Decrypts data |
||||
* @param string $data data to be decrypted. |
||||
* @param string $key the decryption secret key |
||||
* @return string the decrypted data |
||||
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||
* @see encrypt() |
||||
*/ |
||||
public static function decrypt($data, $key) |
||||
{ |
||||
$module = static::openCryptModule(); |
||||
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||
$ivSize = mcrypt_enc_get_iv_size($module); |
||||
$iv = StringHelper::substr($data, 0, $ivSize); |
||||
mcrypt_generic_init($module, $key, $iv); |
||||
$decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data))); |
||||
mcrypt_generic_deinit($module); |
||||
mcrypt_module_close($module); |
||||
return rtrim($decrypted, "\0"); |
||||
} |
||||
|
||||
/** |
||||
* Prefixes data with a keyed hash value so that it can later be detected if it is tampered. |
||||
* @param string $data the data to be protected |
||||
* @param string $key the secret key to be used for generating hash |
||||
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||
* function to see the supported hashing algorithms on your system. |
||||
* @return string the data prefixed with the keyed hash |
||||
* @see validateData() |
||||
* @see getSecretKey() |
||||
*/ |
||||
public static function hashData($data, $key, $algorithm = 'sha256') |
||||
{ |
||||
return hash_hmac($algorithm, $data, $key) . $data; |
||||
} |
||||
|
||||
/** |
||||
* Validates if the given data is tampered. |
||||
* @param string $data the data to be validated. The data must be previously |
||||
* generated by [[hashData()]]. |
||||
* @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]]. |
||||
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||
* function to see the supported hashing algorithms on your system. This must be the same |
||||
* as the value passed to [[hashData()]] when generating the hash for the data. |
||||
* @return string the real data with the hash stripped off. False if the data is tampered. |
||||
* @see hashData() |
||||
*/ |
||||
public static function validateData($data, $key, $algorithm = 'sha256') |
||||
{ |
||||
$hashSize = StringHelper::strlen(hash_hmac($algorithm, 'test', $key)); |
||||
$n = StringHelper::strlen($data); |
||||
if ($n >= $hashSize) { |
||||
$hash = StringHelper::substr($data, 0, $hashSize); |
||||
$data2 = StringHelper::substr($data, $hashSize, $n - $hashSize); |
||||
return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a secret key associated with the specified name. |
||||
* If the secret key does not exist, a random key will be generated |
||||
* and saved in the file "keys.php" under the application's runtime directory |
||||
* so that the same secret key can be returned in future requests. |
||||
* @param string $name the name that is associated with the secret key |
||||
* @param integer $length the length of the key that should be generated if not exists |
||||
* @return string the secret key associated with the specified name |
||||
*/ |
||||
public static function getSecretKey($name, $length = 32) |
||||
{ |
||||
static $keys; |
||||
$keyFile = Yii::$app->getRuntimePath() . '/keys.php'; |
||||
if ($keys === null) { |
||||
$keys = is_file($keyFile) ? require($keyFile) : array(); |
||||
} |
||||
if (!isset($keys[$name])) { |
||||
// generate a 32-char random key |
||||
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
||||
$keys[$name] = substr(str_shuffle(str_repeat($chars, 5)), 0, $length); |
||||
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
|
||||
} |
||||
return $keys[$name]; |
||||
} |
||||
|
||||
/** |
||||
* Opens the mcrypt module. |
||||
* @return resource the mcrypt module handle. |
||||
* @throws InvalidConfigException if mcrypt extension is not installed |
||||
* @throws Exception if mcrypt initialization fails |
||||
*/ |
||||
protected static function openCryptModule() |
||||
{ |
||||
if (!extension_loaded('mcrypt')) { |
||||
throw new InvalidConfigException('The mcrypt PHP extension is not installed.'); |
||||
} |
||||
$module = @mcrypt_module_open('rijndael-256', '', MCRYPT_MODE_CBC, ''); |
||||
if ($module === false) { |
||||
throw new Exception('Failed to initialize the mcrypt module.'); |
||||
} |
||||
return $module; |
||||
} |
||||
|
||||
/** |
||||
* Generates a secure hash from a password and a random salt. |
||||
* |
||||
* The generated hash can be stored in database (e.g. `CHAR(64) CHARACTER SET latin1` on MySQL). |
||||
* Later when a password needs to be validated, the hash can be fetched and passed |
||||
* to [[validatePassword()]]. For example, |
||||
* |
||||
* ~~~ |
||||
* // generates the hash (usually done during user registration or when the password is changed) |
||||
* $hash = SecurityHelper::hashPassword($password); |
||||
* // ...save $hash in database... |
||||
* |
||||
* // during login, validate if the password entered is correct using $hash fetched from database |
||||
* if (PasswordHelper::verifyPassword($password, $hash) { |
||||
* // password is good |
||||
* } else { |
||||
* // password is bad |
||||
* } |
||||
* ~~~ |
||||
* |
||||
* @param string $password The password to be hashed. |
||||
* @param integer $cost Cost parameter used by the Blowfish hash algorithm. |
||||
* The higher the value of cost, |
||||
* the longer it takes to generate the hash and to verify a password against it. Higher cost |
||||
* therefore slows down a brute-force attack. For best protection against brute for attacks, |
||||
* set it to the highest value that is tolerable on production servers. The time taken to |
||||
* compute the hash doubles for every increment by one of $cost. So, for example, if the |
||||
* hash takes 1 second to compute when $cost is 14 then then the compute time varies as |
||||
* 2^($cost - 14) seconds. |
||||
* @throws Exception on bad password parameter or cost parameter |
||||
* @return string The password hash string, ASCII and not longer than 64 characters. |
||||
* @see validatePassword() |
||||
*/ |
||||
public static function generatePasswordHash($password, $cost = 13) |
||||
{ |
||||
$salt = static::generateSalt($cost); |
||||
$hash = crypt($password, $salt); |
||||
|
||||
if (!is_string($hash) || strlen($hash) < 32) { |
||||
throw new Exception('Unknown error occurred while generating hash.'); |
||||
} |
||||
|
||||
return $hash; |
||||
} |
||||
|
||||
/** |
||||
* Verifies a password against a hash. |
||||
* @param string $password The password to verify. |
||||
* @param string $hash The hash to verify the password against. |
||||
* @return boolean whether the password is correct. |
||||
* @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available. |
||||
* @see generatePasswordHash() |
||||
*/ |
||||
public static function validatePassword($password, $hash) |
||||
{ |
||||
if (!is_string($password) || $password === '') { |
||||
throw new InvalidParamException('Password must be a string and cannot be empty.'); |
||||
} |
||||
|
||||
if (!preg_match('/^\$2[axy]\$(\d\d)\$[\./0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) { |
||||
throw new InvalidParamException('Hash is invalid.'); |
||||
} |
||||
|
||||
$test = crypt($password, $hash); |
||||
$n = strlen($test); |
||||
if (strlen($test) < 32 || $n !== strlen($hash)) { |
||||
return false; |
||||
} |
||||
|
||||
// Use a for-loop to compare two strings to prevent timing attacks. See: |
||||
// http://codereview.stackexchange.com/questions/13512 |
||||
$check = 0; |
||||
for ($i = 0; $i < $n; ++$i) { |
||||
$check |= (ord($test[$i]) ^ ord($hash[$i])); |
||||
} |
||||
|
||||
return $check === 0; |
||||
} |
||||
|
||||
/** |
||||
* Generates a salt that can be used to generate a password hash. |
||||
* |
||||
* The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function |
||||
* requires, for the Blowfish hash algorithm, a salt string in a specific format: |
||||
* "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters |
||||
* from the alphabet "./0-9A-Za-z". |
||||
* |
||||
* @param integer $cost the cost parameter |
||||
* @return string the random salt value. |
||||
* @throws InvalidParamException if the cost parameter is not between 4 and 30 |
||||
*/ |
||||
protected static function generateSalt($cost = 13) |
||||
{ |
||||
$cost = (int)$cost; |
||||
if ($cost < 4 || $cost > 30) { |
||||
throw new InvalidParamException('Cost must be between 4 and 31.'); |
||||
} |
||||
|
||||
// Get 20 * 8bits of pseudo-random entropy from mt_rand(). |
||||
$rand = ''; |
||||
for ($i = 0; $i < 20; ++$i) { |
||||
$rand .= chr(mt_rand(0, 255)); |
||||
} |
||||
|
||||
// Add the microtime for a little more entropy. |
||||
$rand .= microtime(); |
||||
// Mix the bits cryptographically into a 20-byte binary string. |
||||
$rand = sha1($rand, true); |
||||
// Form the prefix that specifies Blowfish algorithm and cost parameter. |
||||
$salt = sprintf("$2y$%02d$", $cost); |
||||
// Append the random salt data in the required base64 format. |
||||
$salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22)); |
||||
return $salt; |
||||
} |
||||
} |
@ -0,0 +1,125 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
/** |
||||
* StringHelper |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @author Alex Makarov <sam@rmcreative.ru> |
||||
* @since 2.0 |
||||
*/ |
||||
class StringHelper |
||||
{ |
||||
/** |
||||
* Returns the number of bytes in the given string. |
||||
* This method ensures the string is treated as a byte array. |
||||
* It will use `mb_strlen()` if it is available. |
||||
* @param string $string the string being measured for length |
||||
* @return integer the number of bytes in the given string. |
||||
*/ |
||||
public static function strlen($string) |
||||
{ |
||||
return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string); |
||||
} |
||||
|
||||
/** |
||||
* Returns the portion of string specified by the start and length parameters. |
||||
* This method ensures the string is treated as a byte array. |
||||
* It will use `mb_substr()` if it is available. |
||||
* @param string $string the input string. Must be one character or longer. |
||||
* @param integer $start the starting position |
||||
* @param integer $length the desired portion length |
||||
* @return string the extracted part of string, or FALSE on failure or an empty string. |
||||
* @see http://www.php.net/manual/en/function.substr.php |
||||
*/ |
||||
public static function substr($string, $start, $length) |
||||
{ |
||||
return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length); |
||||
} |
||||
|
||||
/** |
||||
* Converts a word to its plural form. |
||||
* Note that this is for English only! |
||||
* For example, 'apple' will become 'apples', and 'child' will become 'children'. |
||||
* @param string $name the word to be pluralized |
||||
* @return string the pluralized word |
||||
*/ |
||||
public static function pluralize($name) |
||||
{ |
||||
static $rules = array( |
||||
'/(m)ove$/i' => '\1oves', |
||||
'/(f)oot$/i' => '\1eet', |
||||
'/(c)hild$/i' => '\1hildren', |
||||
'/(h)uman$/i' => '\1umans', |
||||
'/(m)an$/i' => '\1en', |
||||
'/(s)taff$/i' => '\1taff', |
||||
'/(t)ooth$/i' => '\1eeth', |
||||
'/(p)erson$/i' => '\1eople', |
||||
'/([m|l])ouse$/i' => '\1ice', |
||||
'/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es', |
||||
'/([^aeiouy]|qu)y$/i' => '\1ies', |
||||
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', |
||||
'/(shea|lea|loa|thie)f$/i' => '\1ves', |
||||
'/([ti])um$/i' => '\1a', |
||||
'/(tomat|potat|ech|her|vet)o$/i' => '\1oes', |
||||
'/(bu)s$/i' => '\1ses', |
||||
'/(ax|test)is$/i' => '\1es', |
||||
'/s$/' => 's', |
||||
); |
||||
foreach ($rules as $rule => $replacement) { |
||||
if (preg_match($rule, $name)) { |
||||
return preg_replace($rule, $replacement, $name); |
||||
} |
||||
} |
||||
return $name . 's'; |
||||
} |
||||
|
||||
/** |
||||
* Converts a CamelCase name into space-separated words. |
||||
* For example, 'PostTag' will be converted to 'Post Tag'. |
||||
* @param string $name the string to be converted |
||||
* @param boolean $ucwords whether to capitalize the first letter in each word |
||||
* @return string the resulting words |
||||
*/ |
||||
public static function camel2words($name, $ucwords = true) |
||||
{ |
||||
$label = trim(strtolower(str_replace(array('-', '_', '.'), ' ', preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))); |
||||
return $ucwords ? ucwords($label) : $label; |
||||
} |
||||
|
||||
/** |
||||
* Converts a CamelCase name into an ID in lowercase. |
||||
* Words in the ID may be concatenated using the specified character (defaults to '-'). |
||||
* For example, 'PostTag' will be converted to 'post-tag'. |
||||
* @param string $name the string to be converted |
||||
* @param string $separator the character used to concatenate the words in the ID |
||||
* @return string the resulting ID |
||||
*/ |
||||
public static function camel2id($name, $separator = '-') |
||||
{ |
||||
if ($separator === '_') { |
||||
return trim(strtolower(preg_replace('/(?<![A-Z])[A-Z]/', '_\0', $name)), '_'); |
||||
} else { |
||||
return trim(strtolower(str_replace('_', $separator, preg_replace('/(?<![A-Z])[A-Z]/', $separator . '\0', $name))), $separator); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Converts an ID into a CamelCase name. |
||||
* Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name. |
||||
* For example, 'post-tag' is converted to 'PostTag'. |
||||
* @param string $id the ID to be converted |
||||
* @param string $separator the character used to separate the words in the ID |
||||
* @return string the resulting CamelCase name |
||||
*/ |
||||
public static function id2camel($id, $separator = '-') |
||||
{ |
||||
return str_replace(' ', '', ucwords(implode(' ', explode($separator, $id)))); |
||||
} |
||||
} |
@ -0,0 +1,134 @@
|
||||
<?php |
||||
/** |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
/** |
||||
* VarDumper is intended to replace the buggy PHP function var_dump and print_r. |
||||
* It can correctly identify the recursively referenced objects in a complex |
||||
* object structure. It also has a recursive depth control to avoid indefinite |
||||
* recursive display of some peculiar variables. |
||||
* |
||||
* VarDumper can be used as follows, |
||||
* |
||||
* ~~~ |
||||
* VarDumper::dump($var); |
||||
* ~~~ |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class VarDumper |
||||
{ |
||||
private static $_objects; |
||||
private static $_output; |
||||
private static $_depth; |
||||
|
||||
/** |
||||
* Displays a variable. |
||||
* This method achieves the similar functionality as var_dump and print_r |
||||
* but is more robust when handling complex objects such as Yii controllers. |
||||
* @param mixed $var variable to be dumped |
||||
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||
* @param boolean $highlight whether the result should be syntax-highlighted |
||||
*/ |
||||
public static function dump($var, $depth = 10, $highlight = false) |
||||
{ |
||||
echo self::dumpAsString($var, $depth, $highlight); |
||||
} |
||||
|
||||
/** |
||||
* Dumps a variable in terms of a string. |
||||
* This method achieves the similar functionality as var_dump and print_r |
||||
* but is more robust when handling complex objects such as Yii controllers. |
||||
* @param mixed $var variable to be dumped |
||||
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||
* @param boolean $highlight whether the result should be syntax-highlighted |
||||
* @return string the string representation of the variable |
||||
*/ |
||||
public static function dumpAsString($var, $depth = 10, $highlight = false) |
||||
{ |
||||
self::$_output = ''; |
||||
self::$_objects = array(); |
||||
self::$_depth = $depth; |
||||
self::dumpInternal($var, 0); |
||||
if ($highlight) { |
||||
$result = highlight_string("<?php\n" . self::$_output, true);
|
||||
self::$_output = preg_replace('/<\\?php<br \\/>/', '', $result, 1); |
||||
} |
||||
return self::$_output; |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $var variable to be dumped |
||||
* @param integer $level depth level |
||||
*/ |
||||
private static function dumpInternal($var, $level) |
||||
{ |
||||
switch (gettype($var)) { |
||||
case 'boolean': |
||||
self::$_output .= $var ? 'true' : 'false'; |
||||
break; |
||||
case 'integer': |
||||
self::$_output .= "$var"; |
||||
break; |
||||
case 'double': |
||||
self::$_output .= "$var"; |
||||
break; |
||||
case 'string': |
||||
self::$_output .= "'" . addslashes($var) . "'"; |
||||
break; |
||||
case 'resource': |
||||
self::$_output .= '{resource}'; |
||||
break; |
||||
case 'NULL': |
||||
self::$_output .= "null"; |
||||
break; |
||||
case 'unknown type': |
||||
self::$_output .= '{unknown}'; |
||||
break; |
||||
case 'array': |
||||
if (self::$_depth <= $level) { |
||||
self::$_output .= 'array(...)'; |
||||
} elseif (empty($var)) { |
||||
self::$_output .= 'array()'; |
||||
} else { |
||||
$keys = array_keys($var); |
||||
$spaces = str_repeat(' ', $level * 4); |
||||
self::$_output .= "array\n" . $spaces . '('; |
||||
foreach ($keys as $key) { |
||||
self::$_output .= "\n" . $spaces . ' '; |
||||
self::dumpInternal($key, 0); |
||||
self::$_output .= ' => '; |
||||
self::dumpInternal($var[$key], $level + 1); |
||||
} |
||||
self::$_output .= "\n" . $spaces . ')'; |
||||
} |
||||
break; |
||||
case 'object': |
||||
if (($id = array_search($var, self::$_objects, true)) !== false) { |
||||
self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)'; |
||||
} elseif (self::$_depth <= $level) { |
||||
self::$_output .= get_class($var) . '(...)'; |
||||
} else { |
||||
$id = self::$_objects[] = $var; |
||||
$className = get_class($var); |
||||
$members = (array)$var; |
||||
$spaces = str_repeat(' ', $level * 4); |
||||
self::$_output .= "$className#$id\n" . $spaces . '('; |
||||
foreach ($members as $key => $value) { |
||||
$keyDisplay = strtr(trim($key), array("\0" => ':')); |
||||
self::$_output .= "\n" . $spaces . " [$keyDisplay] => "; |
||||
self::dumpInternal($value, $level + 1); |
||||
} |
||||
self::$_output .= "\n" . $spaces . ')'; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,176 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\web; |
||||
|
||||
use Yii; |
||||
use yii\base\InvalidConfigException; |
||||
use yii\base\Object; |
||||
|
||||
/** |
||||
* AssetBundle represents a collection of asset files, such as CSS, JS, images. |
||||
* |
||||
* Each asset bundle has a unique name that globally identifies it among all asset bundles |
||||
* used in an application. |
||||
* |
||||
* An asset bundle can depend on other asset bundles. When registering an asset bundle |
||||
* with a view, all its dependent asset bundles will be automatically registered. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class AssetBundle extends Object |
||||
{ |
||||
/** |
||||
* @var string the root directory of the source asset files. A source asset file |
||||
* is a file that is part of your source code repository of your Web application. |
||||
* |
||||
* You must set this property if the directory containing the source asset files |
||||
* is not Web accessible (this is usually the case for extensions). |
||||
* |
||||
* By setting this property, the asset manager will publish the source asset files |
||||
* to a Web-accessible directory [[basePath]]. |
||||
* |
||||
* You can use either a directory or an alias of the directory. |
||||
*/ |
||||
public $sourcePath; |
||||
/** |
||||
* @var string the Web-accessible directory that contains the asset files in this bundle. |
||||
* |
||||
* If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] |
||||
* when it publishes the asset files from [[sourcePath]]. |
||||
* |
||||
* If the bundle contains any assets that are specified in terms of relative file path, |
||||
* then this property must be set either manually or automatically (by asset manager via |
||||
* asset publishing). |
||||
* |
||||
* You can use either a directory or an alias of the directory. |
||||
*/ |
||||
public $basePath; |
||||
/** |
||||
* @var string the base URL that will be prefixed to the asset files for them to |
||||
* be accessed via Web server. |
||||
* |
||||
* If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] |
||||
* when it publishes the asset files from [[sourcePath]]. |
||||
* |
||||
* If the bundle contains any assets that are specified in terms of relative file path, |
||||
* then this property must be set either manually or automatically (by asset manager via |
||||
* asset publishing). |
||||
* |
||||
* You can use either a URL or an alias of the URL. |
||||
*/ |
||||
public $baseUrl; |
||||
/** |
||||
* @var array list of the bundle names that this bundle depends on |
||||
*/ |
||||
public $depends = array(); |
||||
/** |
||||
* @var array list of JavaScript files that this bundle contains. Each JavaScript file can |
||||
* be either a file path (without leading slash) relative to [[basePath]] or a URL representing |
||||
* an external JavaScript file. |
||||
* |
||||
* Note that only forward slash "/" can be used as directory separator. |
||||
*/ |
||||
public $js = array(); |
||||
/** |
||||
* @var array list of CSS files that this bundle contains. Each CSS file can |
||||
* be either a file path (without leading slash) relative to [[basePath]] or a URL representing |
||||
* an external CSS file. |
||||
* |
||||
* Note that only forward slash "/" can be used as directory separator. |
||||
*/ |
||||
public $css = array(); |
||||
/** |
||||
* @var array the options that will be passed to [[\yii\base\View::registerJsFile()]] |
||||
* when registering the JS files in this bundle. |
||||
*/ |
||||
public $jsOptions = array(); |
||||
/** |
||||
* @var array the options that will be passed to [[\yii\base\View::registerCssFile()]] |
||||
* when registering the CSS files in this bundle. |
||||
*/ |
||||
public $cssOptions = array(); |
||||
/** |
||||
* @var array the options to be passed to [[AssetManager::publish()]] when the asset bundle |
||||
* is being published. |
||||
*/ |
||||
public $publishOptions = array(); |
||||
|
||||
/** |
||||
* Initializes the bundle. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
if ($this->sourcePath !== null) { |
||||
$this->sourcePath = rtrim(Yii::getAlias($this->sourcePath), '/\\'); |
||||
} |
||||
if ($this->basePath !== null) { |
||||
$this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\'); |
||||
} |
||||
if ($this->baseUrl !== null) { |
||||
$this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers the CSS and JS files with the given view. |
||||
* This method will first register all dependent asset bundles. |
||||
* It will then try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding |
||||
* CSS or JS files using [[AssetManager::converter|asset converter]]. |
||||
* @param \yii\base\View $view the view that the asset files to be registered with. |
||||
* @throws InvalidConfigException if [[baseUrl]] or [[basePath]] is not set when the bundle |
||||
* contains internal CSS or JS files. |
||||
*/ |
||||
public function registerAssets($view) |
||||
{ |
||||
foreach ($this->depends as $name) { |
||||
$view->registerAssetBundle($name); |
||||
} |
||||
|
||||
$this->publish($view->getAssetManager()); |
||||
|
||||
foreach ($this->js as $js) { |
||||
$view->registerJsFile($js, $this->jsOptions); |
||||
} |
||||
foreach ($this->css as $css) { |
||||
$view->registerCssFile($css, $this->cssOptions); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Publishes the asset bundle if its source code is not under Web-accessible directory. |
||||
* @param AssetManager $am the asset manager to perform the asset publishing |
||||
* @throws InvalidConfigException if [[baseUrl]] or [[basePath]] is not set when the bundle |
||||
* contains internal CSS or JS files. |
||||
*/ |
||||
public function publish($am) |
||||
{ |
||||
if ($this->sourcePath !== null) { |
||||
list ($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions); |
||||
} |
||||
$converter = $am->getConverter(); |
||||
foreach ($this->js as $i => $js) { |
||||
if (strpos($js, '/') !== 0 && strpos($js, '://') === false) { |
||||
if (isset($this->basePath, $this->baseUrl)) { |
||||
$this->js[$i] = $converter->convert($js, $this->basePath, $this->baseUrl); |
||||
} else { |
||||
throw new InvalidConfigException('Both of the "baseUrl" and "basePath" properties must be set.'); |
||||
} |
||||
} |
||||
} |
||||
foreach ($this->css as $i => $css) { |
||||
if (strpos($css, '/') !== 0 && strpos($css, '://') === false) { |
||||
if (isset($this->basePath, $this->baseUrl)) { |
||||
$this->css[$i] = $converter->convert($css, $this->basePath, $this->baseUrl); |
||||
} else { |
||||
throw new InvalidConfigException('Both of the "baseUrl" and "basePath" properties must be set.'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,62 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\web; |
||||
|
||||
use Yii; |
||||
use yii\base\Component; |
||||
|
||||
/** |
||||
* AssetConverter supports conversion of several popular script formats into JS or CSS scripts. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class AssetConverter extends Component implements IAssetConverter |
||||
{ |
||||
/** |
||||
* @var array the commands that are used to perform the asset conversion. |
||||
* The keys are the asset file extension names, and the values are the corresponding |
||||
* target script types (either "css" or "js") and the commands used for the conversion. |
||||
*/ |
||||
public $commands = array( |
||||
'less' => array('css', 'lessc {from} {to}'), |
||||
'scss' => array('css', 'sass {from} {to}'), |
||||
'sass' => array('css', 'sass {from} {to}'), |
||||
'styl' => array('js', 'stylus < {from} > {to}'), |
||||
); |
||||
|
||||
/** |
||||
* Converts a given asset file into a CSS or JS file. |
||||
* @param string $asset the asset file path, relative to $basePath |
||||
* @param string $basePath the directory the $asset is relative to. |
||||
* @param string $baseUrl the URL corresponding to $basePath |
||||
* @return string the URL to the converted asset file. |
||||
*/ |
||||
public function convert($asset, $basePath, $baseUrl) |
||||
{ |
||||
$pos = strrpos($asset, '.'); |
||||
if ($pos !== false) { |
||||
$ext = substr($asset, $pos + 1); |
||||
if (isset($this->commands[$ext])) { |
||||
list ($ext, $command) = $this->commands[$ext]; |
||||
$result = substr($asset, 0, $pos + 1) . $ext; |
||||
if (@filemtime("$basePath/$result") < filemtime("$basePath/$asset")) { |
||||
$output = array(); |
||||
$command = strtr($command, array( |
||||
'{from}' => "$basePath/$asset", |
||||
'{to}' => "$basePath/$result", |
||||
)); |
||||
exec($command, $output); |
||||
Yii::info("Converted $asset into $result: " . implode("\n", $output), __METHOD__); |
||||
return "$baseUrl/$result"; |
||||
} |
||||
} |
||||
} |
||||
return "$baseUrl/$asset"; |
||||
} |
||||
} |
@ -0,0 +1,27 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\web; |
||||
|
||||
/** |
||||
* The IAssetConverter interface must be implemented by asset converter classes. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
interface IAssetConverter |
||||
{ |
||||
/** |
||||
* Converts a given asset file into a CSS or JS file. |
||||
* @param string $asset the asset file path, relative to $basePath |
||||
* @param string $basePath the directory the $asset is relative to. |
||||
* @param string $baseUrl the URL corresponding to $basePath |
||||
* @return string the URL to the converted asset file. If the given asset does not |
||||
* need conversion, "$baseUrl/$asset" should be returned. |
||||
*/ |
||||
public function convert($asset, $basePath, $baseUrl); |
||||
} |
Loading…
Reference in new issue