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