array('rijndael-256', '', 'ofb', '')
.
- *
- * Defaults to 'des', meaning using DES crypt algorithm.
- */
- public $cryptAlgorithm = 'des';
-
- private $_validationKey;
- private $_encryptionKey;
-
- /**
- * @return string a randomly generated private key
- */
- protected function generateRandomKey()
- {
- return sprintf('%08x%08x%08x%08x', mt_rand(), mt_rand(), mt_rand(), mt_rand());
- }
-
- /**
- * @return string the private key used to generate HMAC.
- * If the key is not explicitly set, a random one is generated and returned.
- */
- public function getValidationKey()
- {
- if ($this->_validationKey !== null) {
- return $this->_validationKey;
- } else {
- if (($key = \Yii::$app->getGlobalState(self::STATE_VALIDATION_KEY)) !== null) {
- $this->setValidationKey($key);
- } else {
- $key = $this->generateRandomKey();
- $this->setValidationKey($key);
- \Yii::$app->setGlobalState(self::STATE_VALIDATION_KEY, $key);
- }
- return $this->_validationKey;
- }
- }
-
- /**
- * @param string $value the key used to generate HMAC
- * @throws CException if the key is empty
- */
- public function setValidationKey($value)
- {
- if (!empty($value)) {
- $this->_validationKey = $value;
- } else {
- throw new CException(Yii::t('yii|SecurityManager.validationKey cannot be empty.'));
- }
- }
-
- /**
- * @return string the private key used to encrypt/decrypt data.
- * If the key is not explicitly set, a random one is generated and returned.
- */
- public function getEncryptionKey()
- {
- if ($this->_encryptionKey !== null) {
- return $this->_encryptionKey;
- } else {
- if (($key = \Yii::$app->getGlobalState(self::STATE_ENCRYPTION_KEY)) !== null) {
- $this->setEncryptionKey($key);
- } else {
- $key = $this->generateRandomKey();
- $this->setEncryptionKey($key);
- \Yii::$app->setGlobalState(self::STATE_ENCRYPTION_KEY, $key);
- }
- return $this->_encryptionKey;
- }
- }
-
- /**
- * @param string $value the key used to encrypt/decrypt data.
- * @throws CException if the key is empty
- */
- public function setEncryptionKey($value)
- {
- if (!empty($value)) {
- $this->_encryptionKey = $value;
- } else {
- throw new CException(Yii::t('yii|SecurityManager.encryptionKey cannot be empty.'));
- }
- }
-
- /**
- * This method has been deprecated since version 1.1.3.
- * Please use {@link hashAlgorithm} instead.
- * @return string
- */
- public function getValidation()
- {
- return $this->hashAlgorithm;
- }
-
- /**
- * This method has been deprecated since version 1.1.3.
- * Please use {@link hashAlgorithm} instead.
- * @param string $value -
- */
- public function setValidation($value)
- {
- $this->hashAlgorithm = $value;
- }
-
- /**
- * Encrypts data.
- * @param string $data data to be encrypted.
- * @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
- * @return string the encrypted data
- * @throws CException if PHP Mcrypt extension is not loaded
- */
- public function encrypt($data, $key = null)
- {
- $module = $this->openCryptModule();
- $key = $this->substr($key === null ? md5($this->getEncryptionKey()) : $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 key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
- * @return string the decrypted data
- * @throws CException if PHP Mcrypt extension is not loaded
- */
- public function decrypt($data, $key = null)
- {
- $module = $this->openCryptModule();
- $key = $this->substr($key === null ? md5($this->getEncryptionKey()) : $key, 0, mcrypt_enc_get_key_size($module));
- $ivSize = mcrypt_enc_get_iv_size($module);
- $iv = $this->substr($data, 0, $ivSize);
- mcrypt_generic_init($module, $key, $iv);
- $decrypted = mdecrypt_generic($module, $this->substr($data, $ivSize, $this->strlen($data)));
- mcrypt_generic_deinit($module);
- mcrypt_module_close($module);
- return rtrim($decrypted, "\0");
- }
-
- /**
- * Opens the mcrypt module with the configuration specified in {@link cryptAlgorithm}.
- * @return resource the mycrypt module handle.
- * @since 1.1.3
- */
- protected function openCryptModule()
- {
- if (extension_loaded('mcrypt')) {
- if (is_array($this->cryptAlgorithm)) {
- $module = @call_user_func_array('mcrypt_module_open', $this->cryptAlgorithm);
- } else {
- $module = @mcrypt_module_open($this->cryptAlgorithm, '', MCRYPT_MODE_CBC, '');
- }
-
- if ($module === false) {
- throw new CException(Yii::t('yii|Failed to initialize the mcrypt module.'));
- }
-
- return $module;
- } else {
- throw new CException(Yii::t('yii|SecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.'));
- }
- }
-
- /**
- * Prefixes data with an HMAC.
- * @param string $data data to be hashed.
- * @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
- * @return string data prefixed with HMAC
- */
- public function hashData($data, $key = null)
- {
- return $this->computeHMAC($data, $key) . $data;
- }
-
- /**
- * Validates if data is tampered.
- * @param string $data data to be validated. The data must be previously
- * generated using {@link hashData()}.
- * @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
- * @return string the real data with HMAC stripped off. False if the data
- * is tampered.
- */
- public function validateData($data, $key = null)
- {
- $len = $this->strlen($this->computeHMAC('test'));
- if ($this->strlen($data) >= $len) {
- $hmac = $this->substr($data, 0, $len);
- $data2 = $this->substr($data, $len, $this->strlen($data));
- return $hmac === $this->computeHMAC($data2, $key) ? $data2 : false;
- } else {
- return false;
- }
- }
-
- /**
- * Computes the HMAC for the data with {@link getValidationKey ValidationKey}.
- * @param string $data data to be generated HMAC
- * @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
- * @return string the HMAC for the data
- */
- protected function computeHMAC($data, $key = null)
- {
- if ($key === null) {
- $key = $this->getValidationKey();
- }
-
- if (function_exists('hash_hmac')) {
- return hash_hmac($this->hashAlgorithm, $data, $key);
- }
-
- if (!strcasecmp($this->hashAlgorithm, 'sha1')) {
- $pack = 'H40';
- $func = 'sha1';
- } else {
- $pack = 'H32';
- $func = 'md5';
- }
- if ($this->strlen($key) > 64) {
- $key = pack($pack, $func($key));
- }
- if ($this->strlen($key) < 64) {
- $key = str_pad($key, 64, chr(0));
- }
- $key = $this->substr($key, 0, 64);
- return $func((str_repeat(chr(0x5C), 64) ^ $key) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ $key) . $data)));
- }
-
- /**
- * Returns the length of the given string.
- * If available uses the multibyte string function mb_strlen.
- * @param string $string the string being measured for length
- * @return int the length of the string
- */
- private 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.
- * If available uses the multibyte string function mb_substr
- * @param string $string the input string. Must be one character or longer.
- * @param int $start the starting position
- * @param int $length the desired portion length
- * @return string the extracted part of string, or FALSE on failure or an empty string.
- */
- private function substr($string, $start, $length)
- {
- return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length);
- }
-}
diff --git a/framework/base/Theme.php b/framework/base/Theme.php
index 52d5245..88ecb0a 100644
--- a/framework/base/Theme.php
+++ b/framework/base/Theme.php
@@ -1,9 +1,7 @@
pathMap as $from => $to) {
- $paths[FileHelper::normalizePath($from) . DIRECTORY_SEPARATOR] = FileHelper::normalizePath($to) . DIRECTORY_SEPARATOR;
+ $from = FileHelper::normalizePath(Yii::getAlias($from));
+ $to = FileHelper::normalizePath(Yii::getAlias($to));
+ $paths[$from . DIRECTORY_SEPARATOR] = $to . DIRECTORY_SEPARATOR;
}
$this->pathMap = $paths;
}
@@ -95,7 +96,7 @@ class Theme extends Component
* @param string $path the file to be themed
* @return string the themed file, or the original file if the themed version is not available.
*/
- public function apply($path)
+ public function applyTo($path)
{
$path = FileHelper::normalizePath($path);
foreach ($this->pathMap as $from => $to) {
diff --git a/framework/base/UnknownMethodException.php b/framework/base/UnknownMethodException.php
index b46903f..29bedca 100644
--- a/framework/base/UnknownMethodException.php
+++ b/framework/base/UnknownMethodException.php
@@ -1,9 +1,7 @@
= 0 && $index < $this->_c) { // in case the value is null
return $this->_d[$index];
} else {
- throw new InvalidCallException('Index out of range: ' . $index);
+ throw new InvalidParamException('Index out of range: ' . $index);
}
}
@@ -132,7 +130,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* one step towards the end.
* @param integer $index the specified position.
* @param mixed $item new item to be inserted into the vector
- * @throws InvalidCallException if the index specified is out of range, or the vector is read-only.
+ * @throws InvalidParamException if the index specified is out of range, or the vector is read-only.
*/
public function insertAt($index, $item)
{
@@ -142,7 +140,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
array_splice($this->_d, $index, 0, array($item));
$this->_c++;
} else {
- throw new InvalidCallException('Index out of range: ' . $index);
+ throw new InvalidParamException('Index out of range: ' . $index);
}
}
@@ -169,7 +167,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Removes an item at the specified position.
* @param integer $index the index of the item to be removed.
* @return mixed the removed item.
- * @throws InvalidCallException if the index is out of range, or the vector is read only.
+ * @throws InvalidParamException if the index is out of range, or the vector is read only.
*/
public function removeAt($index)
{
@@ -183,7 +181,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
return $item;
}
} else {
- throw new InvalidCallException('Index out of range: ' . $index);
+ throw new InvalidParamException('Index out of range: ' . $index);
}
}
@@ -242,7 +240,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Copies iterable data into the vector.
* Note, existing data in the vector will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
- * @throws InvalidCallException if data is neither an array nor an object implementing `Traversable`.
+ * @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function copyFrom($data)
{
@@ -257,7 +255,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
$this->add($item);
}
} else {
- throw new InvalidCallException('Data must be either an array or an object implementing Traversable.');
+ throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
}
}
@@ -265,7 +263,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Merges iterable data into the vector.
* New items will be appended to the end of the existing items.
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
- * @throws InvalidCallException if data is neither an array nor an object implementing `Traversable`.
+ * @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data)
{
@@ -277,7 +275,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
$this->add($item);
}
} else {
- throw new InvalidCallException('The data to be merged with must be an array or an object implementing Traversable.');
+ throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
}
}
diff --git a/framework/base/VectorIterator.php b/framework/base/VectorIterator.php
index d1fefad..f83d42d 100644
--- a/framework/base/VectorIterator.php
+++ b/framework/base/VectorIterator.php
@@ -1,9 +1,7 @@
* @since 2.0
*/
@@ -26,134 +24,124 @@ class View extends Component
/**
* @var object the object that owns this view. This can be a controller, a widget, or any other object.
*/
- public $owner;
- /**
- * @var string the layout to be applied when [[render()]] or [[renderContent()]] is called.
- * If not set, it will use the [[Module::layout]] of the currently active module.
- */
- public $layout;
+ public $context;
/**
- * @var string the language that the view should be rendered in. If not set, it will use
- * the value of [[Application::language]].
+ * @var mixed custom parameters that are shared among view templates.
*/
- public $language;
+ public $params;
/**
- * @var string the language that the original view is in. If not set, it will use
- * the value of [[Application::sourceLanguage]].
+ * @var ViewRenderer|array the view renderer object or the configuration array for
+ * creating the view renderer. If not set, view files will be treated as normal PHP files.
*/
- public $sourceLanguage;
+ public $renderer;
/**
- * @var boolean whether to localize the view when possible. Defaults to true.
- * Note that when this is true, if a localized view cannot be found, the original view will be rendered.
- * No error will be reported.
+ * @var Theme|array the theme object or the configuration array for creating the theme.
+ * If not set, it means theming is not enabled.
*/
- public $enableI18N = true;
+ public $theme;
/**
- * @var boolean whether to theme the view when possible. Defaults to true.
- * Note that theming will be disabled if [[Application::theme]] is not set.
+ * @var array a list of named output clips. You can call [[beginClip()]] and [[endClip()]]
+ * to capture small fragments of a view. They can be later accessed at somewhere else
+ * through this property.
*/
- public $enableTheme = true;
+ public $clips;
/**
- * @var mixed custom parameters that are available in the view template
+ * @var Widget[] the widgets that are currently being rendered (not ended). This property
+ * is maintained by [[beginWidget()]] and [[endWidget()]] methods. Do not modify it directly.
*/
- public $params;
-
+ public $widgetStack = array();
/**
- * @var Widget[] the widgets that are currently not ended
+ * @var array a list of currently active fragment cache widgets. This property
+ * is used internally to implement the content caching feature. Do not modify it.
*/
- private $_widgetStack = array();
-
+ public $cacheStack = array();
/**
- * Constructor.
- * @param object $owner the owner of this view. This usually is a controller or a widget.
- * @param array $config name-value pairs that will be used to initialize the object properties
+ * @var array a list of placeholders for embedding dynamic contents. This property
+ * is used internally to implement the content caching feature. Do not modify it.
*/
- public function __construct($owner, $config = array())
- {
- $this->owner = $owner;
- parent::__construct($config);
- }
+ public $dynamicPlaceholders = array();
- /**
- * Renders a view within a layout.
- * This method is similar to [[renderPartial()]] except that if a layout is available,
- * this method will embed the view result into the layout and then return it.
- * @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name.
- * @param array $params the parameters that should be made available in the view. The PHP function `extract()`
- * will be called on this variable to extract the variables from this parameter.
- * @return string the rendering result
- * @throws InvalidConfigException if the view file or layout file cannot be found
- * @see findViewFile()
- * @see findLayoutFile()
- */
- public function render($view, $params = array())
- {
- $content = $this->renderPartial($view, $params);
- return $this->renderContent($content);
- }
/**
- * Renders a text content within a layout.
- * The layout being used is resolved by [[findLayout()]].
- * If no layout is available, the content will be returned back.
- * @param string $content the content to be rendered
- * @return string the rendering result
- * @throws InvalidConfigException if the layout file cannot be found
- * @see findLayoutFile()
+ * Initializes the view component.
*/
- public function renderContent($content)
+ public function init()
{
- $layoutFile = $this->findLayoutFile();
- if ($layoutFile !== false) {
- return $this->renderFile($layoutFile, array('content' => $content));
- } else {
- return $content;
+ parent::init();
+ if (is_array($this->renderer)) {
+ $this->renderer = Yii::createObject($this->renderer);
+ }
+ if (is_array($this->theme)) {
+ $this->theme = Yii::createObject($this->theme);
}
}
/**
* Renders a view.
*
- * The method first finds the actual view file corresponding to the specified view.
- * It then calls [[renderFile()]] to render the view file. The rendering result is returned
- * as a string. If the view file does not exist, an exception will be thrown.
+ * This method will call [[findViewFile()]] to convert the view name into the corresponding view
+ * file path, and it will then call [[renderFile()]] to render the view.
*
- * @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name.
- * @param array $params the parameters that should be made available in the view. The PHP function `extract()`
- * will be called on this variable to extract the variables from this parameter.
+ * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify this parameter.
+ * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @param object $context the context that the view should use for rendering the view. If null,
+ * existing [[context]] will be used.
* @return string the rendering result
- * @throws InvalidCallException if the view file cannot be found
- * @see findViewFile()
+ * @throws InvalidParamException if the view cannot be resolved or the view file does not exist.
+ * @see renderFile
+ * @see findViewFile
*/
- public function renderPartial($view, $params = array())
+ public function render($view, $params = array(), $context = null)
{
- $file = $this->findViewFile($view);
- if ($file !== false) {
- return $this->renderFile($file, $params);
- } else {
- throw new InvalidCallException("Unable to find the view file for view '$view'.");
- }
+ $viewFile = $this->findViewFile($context, $view);
+ return $this->renderFile($viewFile, $params, $context);
}
/**
* Renders a view file.
*
- * If a [[ViewRenderer|view renderer]] is installed, this method will try to use the view renderer
- * to render the view file. Otherwise, it will simply include the view file, capture its output
- * and return it as a string.
+ * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long
+ * as it is available.
+ *
+ * The method will call [[FileHelper::localize()]] to localize the view file.
*
- * @param string $file the view file.
+ * If [[renderer]] is enabled (not null), the method will use it to render the view file.
+ * Otherwise, it will simply include the view file as a normal PHP file, capture its output and
+ * return it as a string.
+ *
+ * @param string $viewFile the view file. This can be either a file path or a path alias.
* @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @param object $context the context that the view should use for rendering the view. If null,
+ * existing [[context]] will be used.
* @return string the rendering result
+ * @throws InvalidParamException if the view file does not exist
*/
- public function renderFile($file, $params = array())
+ public function renderFile($viewFile, $params = array(), $context = null)
{
- $renderer = Yii::$app->getViewRenderer();
- if ($renderer !== null) {
- return $renderer->render($this, $file, $params);
+ $viewFile = Yii::getAlias($viewFile);
+ if (is_file($viewFile)) {
+ if ($this->theme !== null) {
+ $viewFile = $this->theme->applyTo($viewFile);
+ }
+ $viewFile = FileHelper::localize($viewFile);
+ } else {
+ throw new InvalidParamException("The view file does not exist: $viewFile");
+ }
+
+ $oldContext = $this->context;
+ if ($context !== null) {
+ $this->context = $context;
+ }
+
+ if ($this->renderer !== null) {
+ $output = $this->renderer->render($this, $viewFile, $params);
} else {
- return $this->renderPhpFile($file, $params);
+ $output = $this->renderPhpFile($viewFile, $params);
}
+
+ $this->context = $oldContext;
+
+ return $output;
}
/**
@@ -163,6 +151,8 @@ class View extends Component
* It extracts the given parameters and makes them available in the view file.
* The method captures the output of the included view file and returns it as a string.
*
+ * This method should mainly be called by view renderer or [[renderFile()]].
+ *
* @param string $_file_ the view file.
* @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
* @return string the rendering result
@@ -177,6 +167,95 @@ class View extends Component
}
/**
+ * Renders dynamic content returned by the given PHP statements.
+ * This method is mainly used together with content caching (fragment caching and page caching)
+ * when some portions of the content (called *dynamic content*) should not be cached.
+ * The dynamic content must be returned by some PHP statements.
+ * @param string $statements the PHP statements for generating the dynamic content.
+ * @return string the placeholder of the dynamic content, or the dynamic content if there is no
+ * active content cache currently.
+ */
+ public function renderDynamic($statements)
+ {
+ if (!empty($this->cacheStack)) {
+ $n = count($this->dynamicPlaceholders);
+ $placeholder = "";
+ $this->addDynamicPlaceholder($placeholder, $statements);
+ return $placeholder;
+ } else {
+ return $this->evaluateDynamicContent($statements);
+ }
+ }
+
+ /**
+ * Adds a placeholder for dynamic content.
+ * This method is internally used.
+ * @param string $placeholder the placeholder name
+ * @param string $statements the PHP statements for generating the dynamic content
+ */
+ public function addDynamicPlaceholder($placeholder, $statements)
+ {
+ foreach ($this->cacheStack as $cache) {
+ $cache->dynamicPlaceholders[$placeholder] = $statements;
+ }
+ $this->dynamicPlaceholders[$placeholder] = $statements;
+ }
+
+ /**
+ * Evaluates the given PHP statements.
+ * This method is mainly used internally to implement dynamic content feature.
+ * @param string $statements the PHP statements to be evaluated.
+ * @return mixed the return value of the PHP statements.
+ */
+ public function evaluateDynamicContent($statements)
+ {
+ return eval($statements);
+ }
+
+ /**
+ * Finds the view file based on the given view name.
+ *
+ * A view name can be specified in one of the following formats:
+ *
+ * - path alias (e.g. "@app/views/site/index");
+ * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+ * The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+ * - absolute path within module (e.g. "/site/index"): the view name starts with a single slash.
+ * The actual view file will be looked for under the [[Module::viewPath|view path]] of the currently
+ * active module.
+ * - relative path (e.g. "index"): the actual view file will be looked for under [[Controller::viewPath|viewPath]]
+ * of the context object, assuming the context is either a [[Controller]] or a [[Widget]].
+ *
+ * If the view name does not contain a file extension, it will use the default one `.php`.
+ *
+ * @param object $context the view context object
+ * @param string $view the view name or the path alias of the view file.
+ * @return string the view file path. Note that the file may not exist.
+ * @throws InvalidParamException if the view file is an invalid path alias or the context cannot be
+ * used to determine the actual view file corresponding to the specified view.
+ */
+ protected function findViewFile($context, $view)
+ {
+ if (strncmp($view, '@', 1) === 0) {
+ // e.g. "@app/views/main"
+ $file = Yii::getAlias($view);
+ } elseif (strncmp($view, '//', 2) === 0) {
+ // e.g. "//layouts/main"
+ $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+ } elseif (strncmp($view, '/', 1) === 0) {
+ // e.g. "/site/index"
+ $file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+ } elseif ($context instanceof Controller || $context instanceof Widget) {
+ /** @var $context Controller|Widget */
+ $file = $context->getViewPath() . DIRECTORY_SEPARATOR . $view;
+ } else {
+ throw new InvalidParamException("Unable to resolve the view file for '$view'.");
+ }
+
+ return FileHelper::getExtension($file) === '' ? $file . '.php' : $file;
+ }
+
+ /**
* Creates a widget.
* This method will use [[Yii::createObject()]] to create the widget.
* @param string $class the widget class name or path alias
@@ -186,7 +265,7 @@ class View extends Component
public function createWidget($class, $properties = array())
{
$properties['class'] = $class;
- return Yii::createObject($properties, $this->owner);
+ return Yii::createObject($properties, $this->context);
}
/**
@@ -225,7 +304,7 @@ class View extends Component
public function beginWidget($class, $properties = array())
{
$widget = $this->createWidget($class, $properties);
- $this->_widgetStack[] = $widget;
+ $this->widgetStack[] = $widget;
return $widget;
}
@@ -235,260 +314,108 @@ class View extends Component
* If you want to capture the rendering result of a widget, you may use
* [[createWidget()]] and [[Widget::run()]].
* @return Widget the widget instance
- * @throws Exception if [[beginWidget()]] and [[endWidget()]] calls are not properly nested
+ * @throws InvalidCallException if [[beginWidget()]] and [[endWidget()]] calls are not properly nested
*/
public function endWidget()
{
- $widget = array_pop($this->_widgetStack);
+ $widget = array_pop($this->widgetStack);
if ($widget instanceof Widget) {
$widget->run();
return $widget;
} else {
- throw new Exception("Unmatched beginWidget() and endWidget() calls.");
+ throw new InvalidCallException("Unmatched beginWidget() and endWidget() calls.");
}
}
-//
-// /**
-// * Begins recording a clip.
-// * This method is a shortcut to beginning [[yii\widgets\Clip]]
-// * @param string $id the clip ID.
-// * @param array $properties initial property values for [[yii\widgets\Clip]]
-// */
-// public function beginClip($id, $properties = array())
-// {
-// $properties['id'] = $id;
-// $this->beginWidget('yii\widgets\Clip', $properties);
-// }
-//
-// /**
-// * Ends recording a clip.
-// */
-// public function endClip()
-// {
-// $this->endWidget();
-// }
-//
-// /**
-// * Begins fragment caching.
-// * This method will display cached content if it is available.
-// * If not, it will start caching and would expect an [[endCache()]]
-// * call to end the cache and save the content into cache.
-// * A typical usage of fragment caching is as follows,
-// *
-// * ~~~
-// * if($this->beginCache($id)) {
-// * // ...generate content here
-// * $this->endCache();
-// * }
-// * ~~~
-// *
-// * @param string $id a unique ID identifying the fragment to be cached.
-// * @param array $properties initial property values for [[yii\widgets\OutputCache]]
-// * @return boolean whether we need to generate content for caching. False if cached version is available.
-// * @see endCache
-// */
-// public function beginCache($id, $properties = array())
-// {
-// $properties['id'] = $id;
-// $cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
-// if ($cache->getIsContentCached()) {
-// $this->endCache();
-// return false;
-// } else {
-// return true;
-// }
-// }
-//
-// /**
-// * Ends fragment caching.
-// * This is an alias to [[endWidget()]]
-// * @see beginCache
-// */
-// public function endCache()
-// {
-// $this->endWidget();
-// }
-//
-// /**
-// * Begins the rendering of content that is to be decorated by the specified view.
-// * @param mixed $view the name of the view that will be used to decorate the content. The actual view script
-// * is resolved via {@link getViewFile}. If this parameter is null (default),
-// * the default layout will be used as the decorative view.
-// * Note that if the current controller does not belong to
-// * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
-// * If the controller belongs to a module, the default layout refers to the module's
-// * {@link CWebModule::layout default layout}.
-// * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
-// * @see endContent
-// * @see yii\widgets\ContentDecorator
-// */
-// public function beginContent($view, $params = array())
-// {
-// $this->beginWidget('yii\widgets\ContentDecorator', array(
-// 'view' => $view,
-// 'params' => $params,
-// ));
-// }
-//
-// /**
-// * Ends the rendering of content.
-// * @see beginContent
-// */
-// public function endContent()
-// {
-// $this->endWidget();
-// }
/**
- * Finds the view file based on the given view name.
- *
- * A view name can be specified in one of the following formats:
- *
- * - path alias (e.g. "@app/views/site/index");
- * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
- * The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
- * - absolute path within module (e.g. "/site/index"): the view name starts with a single slash.
- * The actual view file will be looked for under the [[Module::viewPath|view path]] of the currently
- * active module.
- * - relative path (e.g. "index"): the actual view file will be looked for under the [[owner]]'s view path.
- * If [[owner]] is a widget or a controller, its view path is given by their `viewPath` property.
- * If [[owner]] is an object of any other type, its view path is the `view` sub-directory of the directory
- * containing the owner class file.
- *
- * If the view name does not contain a file extension, it will default to `.php`.
- *
- * If [[enableTheme]] is true and there is an active application them, the method will also
- * attempt to use a themed version of the view file, when available.
- *
- * And if [[enableI18N]] is true, the method will attempt to use a translated version of the view file,
- * when available.
- *
- * @param string $view the view name or path alias. If the view name does not specify
- * the view file extension name, it will use `.php` as the extension name.
- * @return string the view file path if it exists. False if the view file cannot be found.
- * @throws InvalidConfigException if the view file does not exist
+ * Begins recording a clip.
+ * This method is a shortcut to beginning [[yii\widgets\Clip]]
+ * @param string $id the clip ID.
+ * @param boolean $renderInPlace whether to render the clip content in place.
+ * Defaults to false, meaning the captured clip will not be displayed.
+ * @return \yii\widgets\Clip the Clip widget instance
+ * @see \yii\widgets\Clip
*/
- public function findViewFile($view)
+ public function beginClip($id, $renderInPlace = false)
{
- if (FileHelper::getExtension($view) === '') {
- $view .= '.php';
- }
- if (strncmp($view, '@', 1) === 0) {
- // e.g. "@app/views/common"
- if (($file = Yii::getAlias($view)) === false) {
- throw new InvalidConfigException("Invalid path alias: $view");
- }
- } elseif (strncmp($view, '/', 1) !== 0) {
- // e.g. "index"
- if ($this->owner instanceof Controller || $this->owner instanceof Widget) {
- $file = $this->owner->getViewPath() . DIRECTORY_SEPARATOR . $view;
- } elseif ($this->owner !== null) {
- $class = new \ReflectionClass($this->owner);
- $file = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $view;
- } else {
- $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . $view;
- }
- } elseif (strncmp($view, '//', 2) !== 0 && Yii::$app->controller !== null) {
- // e.g. "/site/index"
- $file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- } else {
- // e.g. "//layouts/main"
- $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- }
+ return $this->beginWidget('yii\widgets\Clip', array(
+ 'id' => $id,
+ 'renderInPlace' => $renderInPlace,
+ 'view' => $this,
+ ));
+ }
- if (is_file($file)) {
- if ($this->enableTheme && ($theme = Yii::$app->getTheme()) !== null) {
- $file = $theme->apply($file);
- }
- return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
- } else {
- throw new InvalidConfigException("View file for view '$view' does not exist: $file");
- }
+ /**
+ * Ends recording a clip.
+ */
+ public function endClip()
+ {
+ $this->endWidget();
}
/**
- * Finds the layout file that can be applied to the view.
- *
- * The applicable layout is resolved according to the following rules:
- *
- * - If [[layout]] is specified as a string, use it as the layout name and search for the layout file
- * under the layout path of the currently active module;
- * - If [[layout]] is null and [[owner]] is a controller:
- * * If the controller's [[Controller::layout|layout]] is a string, use it as the layout name
- * and search for the layout file under the layout path of the parent module of the controller;
- * * If the controller's [[Controller::layout|layout]] is null, look through its ancestor modules
- * and find the first one whose [[Module::layout|layout]] is not null. Use the layout specified
- * by that module;
- * - Returns false for all other cases.
- *
- * Like view names, a layout name can take several formats:
- *
- * - path alias (e.g. "@app/views/layouts/main");
- * - absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be
- * looked for under the [[Application::layoutPath|layout path]] of the application;
- * - relative path (e.g. "main"): the actual layout layout file will be looked for under the
- * [[Module::viewPath|view path]] of the context module determined by the above layout resolution process.
- *
- * If the layout name does not contain a file extension, it will default to `.php`.
- *
- * If [[enableTheme]] is true and there is an active application them, the method will also
- * attempt to use a themed version of the layout file, when available.
+ * Begins the rendering of content that is to be decorated by the specified view.
+ * @param string $view the name of the view that will be used to decorate the content enclosed by this widget.
+ * Please refer to [[View::findViewFile()]] on how to set this property.
+ * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
+ * @return \yii\widgets\ContentDecorator the ContentDecorator widget instance
+ * @see \yii\widgets\ContentDecorator
+ */
+ public function beginContent($view, $params = array())
+ {
+ return $this->beginWidget('yii\widgets\ContentDecorator', array(
+ 'view' => $this,
+ 'viewName' => $view,
+ 'params' => $params,
+ ));
+ }
+
+ /**
+ * Ends the rendering of content.
+ */
+ public function endContent()
+ {
+ $this->endWidget();
+ }
+
+ /**
+ * Begins fragment caching.
+ * This method will display cached content if it is available.
+ * If not, it will start caching and would expect an [[endCache()]]
+ * call to end the cache and save the content into cache.
+ * A typical usage of fragment caching is as follows,
*
- * And if [[enableI18N]] is true, the method will attempt to use a translated version of the layout file,
- * when available.
+ * ~~~
+ * if($this->beginCache($id)) {
+ * // ...generate content here
+ * $this->endCache();
+ * }
+ * ~~~
*
- * @return string|boolean the layout file path, or false if layout is not needed.
- * @throws InvalidConfigException if the layout file cannot be found
+ * @param string $id a unique ID identifying the fragment to be cached.
+ * @param array $properties initial property values for [[\yii\widgets\FragmentCache]]
+ * @return boolean whether you should generate the content for caching.
+ * False if the cached version is available.
*/
- public function findLayoutFile()
+ public function beginCache($id, $properties = array())
{
- /** @var $module Module */
- if (is_string($this->layout)) {
- if (Yii::$app->controller) {
- $module = Yii::$app->controller->module;
- } else {
- $module = Yii::$app;
- }
- $view = $this->layout;
- } elseif ($this->owner instanceof Controller) {
- if (is_string($this->owner->layout)) {
- $module = $this->owner->module;
- $view = $this->owner->layout;
- } elseif ($this->owner->layout === null) {
- $module = $this->owner->module;
- while ($module !== null && $module->layout === null) {
- $module = $module->module;
- }
- if ($module !== null && is_string($module->layout)) {
- $view = $module->layout;
- }
- }
- }
-
- if (!isset($view)) {
+ $properties['id'] = $id;
+ $properties['view'] = $this;
+ /** @var $cache \yii\widgets\FragmentCache */
+ $cache = $this->beginWidget('yii\widgets\FragmentCache', $properties);
+ if ($cache->getCachedContent() !== false) {
+ $this->endCache();
return false;
- }
-
- if (FileHelper::getExtension($view) === '') {
- $view .= '.php';
- }
- if (strncmp($view, '@', 1) === 0) {
- if (($file = Yii::getAlias($view)) === false) {
- throw new InvalidConfigException("Invalid path alias: $view");
- }
- } elseif (strncmp($view, '/', 1) === 0) {
- $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
} else {
- $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
+ return true;
}
+ }
- if (is_file($file)) {
- if ($this->enableTheme && ($theme = Yii::$app->getTheme()) !== null) {
- $file = $theme->apply($file);
- }
- return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
- } else {
- throw new InvalidConfigException("Layout file for layout '$view' does not exist: $file");
- }
+ /**
+ * Ends fragment caching.
+ */
+ public function endCache()
+ {
+ $this->endWidget();
}
}
\ No newline at end of file
diff --git a/framework/base/ViewRenderer.php b/framework/base/ViewRenderer.php
index ecb216d..576bbe8 100644
--- a/framework/base/ViewRenderer.php
+++ b/framework/base/ViewRenderer.php
@@ -1,9 +1,7 @@
createView()->renderPartial($view, $params);
+ return Yii::$app->getView()->render($view, $params, $this);
}
/**
- * @return View
+ * Renders a view file.
+ * @param string $file the view file to be rendered. This can be either a file path or a path alias.
+ * @param array $params the parameters (name-value pairs) that should be made available in the view.
+ * @return string the rendering result.
+ * @throws InvalidParamException if the view file does not exist.
*/
- public function createView()
+ public function renderFile($file, $params = array())
{
- return new View($this);
+ return Yii::$app->getView()->renderFile($file, $params, $this);
}
/**
diff --git a/framework/caching/ApcCache.php b/framework/caching/ApcCache.php
index b4df296..dd954cc 100644
--- a/framework/caching/ApcCache.php
+++ b/framework/caching/ApcCache.php
@@ -1,9 +1,7 @@
buildKey($className, $method, $id);
* ~~~
*
- * @param string $key the first parameter
+ * @param array|string $key the key to be normalized
* @return string the generated cache key
*/
public function buildKey($key)
{
- if (func_num_args() === 1 && ctype_alnum($key) && strlen($key) <= 32) {
- return (string)$key;
+ if (is_string($key)) {
+ return ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key);
} else {
- $params = func_get_args();
- return md5(serialize($params));
+ return md5(json_encode($key));
}
}
diff --git a/framework/caching/ChainedDependency.php b/framework/caching/ChainedDependency.php
index 570715d..9c4e547 100644
--- a/framework/caching/ChainedDependency.php
+++ b/framework/caching/ChainedDependency.php
@@ -1,9 +1,7 @@
* @since 2.0
@@ -29,23 +27,25 @@ class DbDependency extends Dependency
*/
public $connectionID = 'db';
/**
- * @var Query the SQL query whose result is used to determine if the dependency has been changed.
+ * @var string the SQL query whose result is used to determine if the dependency has been changed.
* Only the first row of the query result will be used.
*/
- public $query;
+ public $sql;
/**
- * @var Connection the DB connection instance
+ * @var array the parameters (name=>value) to be bound to the SQL statement specified by [[sql]].
*/
- private $_db;
+ public $params;
/**
* Constructor.
- * @param Query $query the SQL query whose result is used to determine if the dependency has been changed.
+ * @param string $sql the SQL query whose result is used to determine if the dependency has been changed.
+ * @param array $params the parameters (name=>value) to be bound to the SQL statement specified by [[sql]].
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($query = null, $config = array())
+ public function __construct($sql, $params = array(), $config = array())
{
- $this->query = $query;
+ $this->sql = $sql;
+ $this->params = $params;
parent::__construct($config);
}
@@ -68,22 +68,23 @@ class DbDependency extends Dependency
protected function generateDependencyData()
{
$db = $this->getDb();
- /**
- * @var \yii\db\Command $command
- */
- $command = $this->query->createCommand($db);
if ($db->enableQueryCache) {
// temporarily disable and re-enable query caching
$db->enableQueryCache = false;
- $result = $command->queryRow();
+ $result = $db->createCommand($this->sql, $this->params)->queryRow();
$db->enableQueryCache = true;
} else {
- $result = $command->queryRow();
+ $result = $db->createCommand($this->sql, $this->params)->queryRow();
}
return $result;
}
/**
+ * @var Connection the DB connection instance
+ */
+ private $_db;
+
+ /**
* Returns the DB connection instance used for caching purpose.
* @return Connection the DB connection instance
* @throws InvalidConfigException if [[connectionID]] does not point to a valid application component.
@@ -91,11 +92,11 @@ class DbDependency extends Dependency
public function getDb()
{
if ($this->_db === null) {
- $db = \Yii::$app->getComponent($this->connectionID);
+ $db = Yii::$app->getComponent($this->connectionID);
if ($db instanceof Connection) {
$this->_db = $db;
} else {
- throw new InvalidConfigException("DbCache::connectionID must refer to the ID of a DB application component.");
+ throw new InvalidConfigException("DbCacheDependency::connectionID must refer to the ID of a DB application component.");
}
}
return $this->_db;
diff --git a/framework/caching/Dependency.php b/framework/caching/Dependency.php
index 2e66145..feb8c07 100644
--- a/framework/caching/Dependency.php
+++ b/framework/caching/Dependency.php
@@ -1,9 +1,7 @@
cachePath = \Yii::getAlias($this->cachePath);
- if ($this->cachePath === false) {
- throw new InvalidConfigException('FileCache.cachePath must be a valid path alias.');
- }
if (!is_dir($this->cachePath)) {
mkdir($this->cachePath, 0777, true);
}
diff --git a/framework/caching/FileDependency.php b/framework/caching/FileDependency.php
index 89b356c..3797dde 100644
--- a/framework/caching/FileDependency.php
+++ b/framework/caching/FileDependency.php
@@ -1,9 +1,7 @@
getRequest();
if ($request->getIsConsoleRequest()) {
- return $this->runAction($request->route, $request->params);
+ list ($route, $params) = $request->resolve();
+ return $this->runAction($route, $params);
} else {
- throw new Exception(\Yii::t('yii|this script must be run from the command line.'));
+ throw new Exception(\Yii::t('yii|This script must be run from the command line.'));
}
}
@@ -126,7 +127,7 @@ class Application extends \yii\base\Application
'message' => 'yii\console\controllers\MessageController',
'help' => 'yii\console\controllers\HelpController',
'migrate' => 'yii\console\controllers\MigrateController',
- 'app' => 'yii\console\controllers\CreateController',
+ 'app' => 'yii\console\controllers\AppController',
'cache' => 'yii\console\controllers\CacheController',
);
}
diff --git a/framework/console/Controller.php b/framework/console/Controller.php
index ff84a45..b9b0523 100644
--- a/framework/console/Controller.php
+++ b/framework/console/Controller.php
@@ -1,9 +1,7 @@
resolveRequest();
- }
-
public function getRawParams()
{
return isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
}
- protected function resolveRequest()
+ /**
+ * Resolves the current request into a route and the associated parameters.
+ * @return array the first element is the route, and the second is the associated parameters.
+ */
+ public function resolve()
{
$rawParams = $this->getRawParams();
array_shift($rawParams); // the 1st argument is the yiic script name
if (isset($rawParams[0])) {
- $this->route = $rawParams[0];
+ $route = $rawParams[0];
array_shift($rawParams);
} else {
- $this->route = '';
+ $route = '';
}
- $this->params = array(self::ANONYMOUS_PARAMS => array());
+ $params = array(self::ANONYMOUS_PARAMS => array());
foreach ($rawParams as $param) {
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
$name = $matches[1];
- $this->params[$name] = isset($matches[3]) ? $matches[3] : true;
+ $params[$name] = isset($matches[3]) ? $matches[3] : true;
} else {
- $this->params[self::ANONYMOUS_PARAMS][] = $param;
+ $params[self::ANONYMOUS_PARAMS][] = $param;
}
}
+
+ return array($route, $params);
}
}
diff --git a/framework/console/controllers/CreateController.php b/framework/console/controllers/AppController.php
similarity index 91%
rename from framework/console/controllers/CreateController.php
rename to framework/console/controllers/AppController.php
index 7bd7fd0..93ef5f5 100644
--- a/framework/console/controllers/CreateController.php
+++ b/framework/console/controllers/AppController.php
@@ -1,16 +1,14 @@
* @since 2.0
*/
-class CreateController extends Controller
+class AppController extends Controller
{
private $_rootPath;
private $_config;
/**
* @var string custom template path. If specified, templates will be
- * searched there additionally to `framework/console/create`.
+ * searched there additionally to `framework/console/webapp`.
*/
public $templatesPath;
@@ -46,6 +44,16 @@ class CreateController extends Controller
}
}
+ public function globalOptions()
+ {
+ return array('templatesPath', 'type');
+ }
+
+ public function actionIndex()
+ {
+ $this->forward('help/index', array('-args' => array('app/create')));
+ }
+
/**
* Generates Yii application at the path specified via appPath parameter.
*
@@ -56,7 +64,7 @@ class CreateController extends Controller
* @throws \yii\base\Exception if path specified is not valid
* @return integer the exit status
*/
- public function actionIndex($path)
+ public function actionCreate($path)
{
$path = strtr($path, '/\\', DIRECTORY_SEPARATOR);
if(strpos($path, DIRECTORY_SEPARATOR) === false) {
@@ -127,7 +135,7 @@ class CreateController extends Controller
*/
protected function getDefaultTemplatesPath()
{
- return realpath(__DIR__.'/../create');
+ return realpath(__DIR__.'/../webapp');
}
/**
diff --git a/framework/console/controllers/CacheController.php b/framework/console/controllers/CacheController.php
index 866db12..6765f9b 100644
--- a/framework/console/controllers/CacheController.php
+++ b/framework/console/controllers/CacheController.php
@@ -1,9 +1,7 @@
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php
index 59dcb3f..7f9a18f 100644
--- a/framework/console/controllers/MigrateController.php
+++ b/framework/console/controllers/MigrateController.php
@@ -1,10 +1,8 @@
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
@@ -15,7 +13,7 @@ use yii\console\Exception;
use yii\console\Controller;
use yii\db\Connection;
use yii\db\Query;
-use yii\util\ArrayHelper;
+use yii\helpers\ArrayHelper;
/**
* This command manages application migrations.
@@ -116,7 +114,7 @@ class MigrateController extends Controller
{
if (parent::beforeAction($action)) {
$path = Yii::getAlias($this->migrationPath);
- if ($path === false || !is_dir($path)) {
+ if (!is_dir($path)) {
throw new Exception("The migration directory \"{$this->migrationPath}\" does not exist.");
}
$this->migrationPath = $path;
diff --git a/framework/console/create/config.php b/framework/console/webapp/config.php
similarity index 83%
rename from framework/console/create/config.php
rename to framework/console/webapp/config.php
index 29f0b0b..112fb18 100644
--- a/framework/console/create/config.php
+++ b/framework/console/webapp/config.php
@@ -1,5 +1,5 @@
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php
index 0b2451f..0c15121 100644
--- a/framework/db/ActiveRecord.php
+++ b/framework/db/ActiveRecord.php
@@ -1,24 +1,22 @@
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
use yii\base\Model;
-use yii\base\Event;
+use yii\base\InvalidParamException;
use yii\base\ModelEvent;
use yii\base\UnknownMethodException;
use yii\base\InvalidCallException;
use yii\db\Connection;
use yii\db\TableSchema;
use yii\db\Expression;
-use yii\util\StringHelper;
+use yii\helpers\StringHelper;
/**
* ActiveRecord is the base class for classes representing relational data in terms of objects.
@@ -1045,7 +1043,7 @@ class ActiveRecord extends Model
* It can be declared in either the Active Record class itself or one of its behaviors.
* @param string $name the relation name
* @return ActiveRelation the relation object
- * @throws InvalidCallException if the named relation does not exist.
+ * @throws InvalidParamException if the named relation does not exist.
*/
public function getRelation($name)
{
@@ -1057,7 +1055,7 @@ class ActiveRecord extends Model
}
} catch (UnknownMethodException $e) {
}
- throw new InvalidCallException(get_class($this) . ' has no relation named "' . $name . '".');
+ throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
}
/**
diff --git a/framework/db/ActiveRelation.php b/framework/db/ActiveRelation.php
index 54c6c62..f1b198b 100644
--- a/framework/db/ActiveRelation.php
+++ b/framework/db/ActiveRelation.php
@@ -1,10 +1,8 @@
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
@@ -57,16 +55,16 @@ class ActiveRelation extends ActiveQuery
/**
* Specifies the relation associated with the pivot table.
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
- * @param callback $callback a PHP callback for customizing the relation associated with the pivot table.
+ * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation the relation object itself.
*/
- public function via($relationName, $callback = null)
+ public function via($relationName, $callable = null)
{
$relation = $this->primaryModel->getRelation($relationName);
$this->via = array($relationName, $relation);
- if ($callback !== null) {
- call_user_func($callback, $relation);
+ if ($callable !== null) {
+ call_user_func($callable, $relation);
}
return $this;
}
@@ -77,11 +75,11 @@ class ActiveRelation extends ActiveQuery
* @param array $link the link between the pivot table and the table associated with [[primaryModel]].
* The keys of the array represent the columns in the pivot table, and the values represent the columns
* in the [[primaryModel]] table.
- * @param callback $callback a PHP callback for customizing the relation associated with the pivot table.
+ * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation
*/
- public function viaTable($tableName, $link, $callback = null)
+ public function viaTable($tableName, $link, $callable = null)
{
$relation = new ActiveRelation(array(
'modelClass' => get_class($this->primaryModel),
@@ -91,8 +89,8 @@ class ActiveRelation extends ActiveQuery
'asArray' => true,
));
$this->via = $relation;
- if ($callback !== null) {
- call_user_func($callback, $relation);
+ if ($callable !== null) {
+ call_user_func($callable, $relation);
}
return $this;
}
diff --git a/framework/db/ColumnSchema.php b/framework/db/ColumnSchema.php
index 44e6cb0..ffdafd4 100644
--- a/framework/db/ColumnSchema.php
+++ b/framework/db/ColumnSchema.php
@@ -1,9 +1,7 @@
getMessage() . "\nFailed to prepare SQL: $sql", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($e->getMessage(), (int)$e->getCode(), $errorInfo);
+ throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode());
}
}
}
@@ -294,7 +292,7 @@ class Command extends \yii\base\Component
\Yii::error("$message\nFailed to execute SQL: {$sql}{$paramLog}", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($message, (int)$e->getCode(), $errorInfo);
+ throw new Exception($message, $errorInfo, (int)$e->getCode());
}
}
@@ -391,7 +389,13 @@ class Command extends \yii\base\Component
}
if (isset($cache)) {
- $cacheKey = $cache->buildKey(__CLASS__, $db->dsn, $db->username, $sql, $paramLog);
+ $cacheKey = $cache->buildKey(array(
+ __CLASS__,
+ $db->dsn,
+ $db->username,
+ $sql,
+ $paramLog,
+ ));
if (($result = $cache->get($cacheKey)) !== false) {
\Yii::trace('Query result found in cache', __CLASS__);
return $result;
@@ -433,7 +437,7 @@ class Command extends \yii\base\Component
$message = $e->getMessage();
\Yii::error("$message\nCommand::$method() failed: {$sql}{$paramLog}", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($message, (int)$e->getCode(), $errorInfo);
+ throw new Exception($message, $errorInfo, (int)$e->getCode());
}
}
diff --git a/framework/db/Connection.php b/framework/db/Connection.php
index 3564361..40164a3 100644
--- a/framework/db/Connection.php
+++ b/framework/db/Connection.php
@@ -1,9 +1,7 @@
pdo === null) {
if (empty($this->dsn)) {
- throw new InvalidConfigException('Connection.dsn cannot be empty.');
+ throw new InvalidConfigException('Connection::dsn cannot be empty.');
}
try {
\Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__);
@@ -332,7 +330,7 @@ class Connection extends Component
catch (\PDOException $e) {
\Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __CLASS__);
$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
- throw new Exception($message, (int)$e->getCode(), $e->errorInfo);
+ throw new Exception($message, $e->errorInfo, (int)$e->getCode());
}
}
}
diff --git a/framework/db/DataReader.php b/framework/db/DataReader.php
index 8e5291e..20444e7 100644
--- a/framework/db/DataReader.php
+++ b/framework/db/DataReader.php
@@ -1,9 +1,7 @@
errorInfo = $errorInfo;
- parent::__construct($message, $code);
+ parent::__construct($message, $code, $previous);
}
/**
diff --git a/framework/db/Expression.php b/framework/db/Expression.php
index 23fb13e..4ebcd5f 100644
--- a/framework/db/Expression.php
+++ b/framework/db/Expression.php
@@ -1,9 +1,7 @@
select = $columns;
$this->selectOption = $option;
return $this;
@@ -163,6 +186,9 @@ class Query extends \yii\base\Component
*/
public function from($tables)
{
+ if (!is_array($tables)) {
+ $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
+ }
$this->from = $tables;
return $this;
}
@@ -362,10 +388,13 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
- * @see addGroup()
+ * @see addGroupBy()
*/
public function groupBy($columns)
{
+ if (!is_array($columns)) {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ }
$this->groupBy = $columns;
return $this;
}
@@ -377,19 +406,16 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
- * @see group()
+ * @see groupBy()
*/
- public function addGroup($columns)
+ public function addGroupBy($columns)
{
- if (empty($this->groupBy)) {
+ if (!is_array($columns)) {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ }
+ if ($this->groupBy === null) {
$this->groupBy = $columns;
} else {
- if (!is_array($this->groupBy)) {
- $this->groupBy = preg_split('/\s*,\s*/', trim($this->groupBy), -1, PREG_SPLIT_NO_EMPTY);
- }
- if (!is_array($columns)) {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
$this->groupBy = array_merge($this->groupBy, $columns);
}
return $this;
@@ -456,43 +482,58 @@ class Query extends \yii\base\Component
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
- * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')).
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
- * @see addOrder()
+ * @see addOrderBy()
*/
public function orderBy($columns)
{
- $this->orderBy = $columns;
+ $this->orderBy = $this->normalizeOrderBy($columns);
return $this;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
- * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')).
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
- * @see order()
+ * @see orderBy()
*/
public function addOrderBy($columns)
{
- if (empty($this->orderBy)) {
+ $columns = $this->normalizeOrderBy($columns);
+ if ($this->orderBy === null) {
$this->orderBy = $columns;
} else {
- if (!is_array($this->orderBy)) {
- $this->orderBy = preg_split('/\s*,\s*/', trim($this->orderBy), -1, PREG_SPLIT_NO_EMPTY);
- }
- if (!is_array($columns)) {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
+ protected function normalizeOrderBy($columns)
+ {
+ if (is_array($columns)) {
+ return $columns;
+ } else {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ $result = array();
+ foreach ($columns as $column) {
+ if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
+ $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC;
+ } else {
+ $result[$column] = self::SORT_ASC;
+ }
+ }
+ return $result;
+ }
+ }
+
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php
index ebca888..75375cc 100644
--- a/framework/db/QueryBuilder.php
+++ b/framework/db/QueryBuilder.php
@@ -1,9 +1,7 @@
buildFrom($query->from),
$this->buildJoin($query->join),
$this->buildWhere($query->where),
- $this->buildGroup($query->groupBy),
+ $this->buildGroupBy($query->groupBy),
$this->buildHaving($query->having),
$this->buildUnion($query->union),
- $this->buildOrder($query->orderBy),
+ $this->buildOrderBy($query->orderBy),
$this->buildLimit($query->limit, $query->offset),
);
return implode($this->separator, array_filter($clauses));
@@ -592,21 +590,19 @@ class QueryBuilder extends \yii\base\Object
return $operator === 'IN' ? '0=1' : '';
}
- if (is_array($column)) {
- if (count($column) > 1) {
- return $this->buildCompositeInCondition($operator, $column, $values);
+ if (count($column) > 1) {
+ return $this->buildCompositeInCondition($operator, $column, $values);
+ } elseif (is_array($column)) {
+ $column = reset($column);
+ }
+ foreach ($values as $i => $value) {
+ if (is_array($value)) {
+ $value = isset($value[$column]) ? $value[$column] : null;
+ }
+ if ($value === null) {
+ $values[$i] = 'NULL';
} else {
- $column = reset($column);
- foreach ($values as $i => $value) {
- if (is_array($value)) {
- $value = isset($value[$column]) ? $value[$column] : null;
- }
- if ($value === null) {
- $values[$i] = 'NULL';
- } else {
- $values[$i] = is_string($value) ? $this->db->quoteValue($value) : (string)$value;
- }
- }
+ $values[$i] = is_string($value) ? $this->db->quoteValue($value) : (string)$value;
}
}
if (strpos($column, '(') === false) {
@@ -677,7 +673,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
- * @param string|array $columns
+ * @param array $columns
* @param boolean $distinct
* @param string $selectOption
* @return string the SELECT clause built from [[query]].
@@ -693,13 +689,6 @@ class QueryBuilder extends \yii\base\Object
return $select . ' *';
}
- if (!is_array($columns)) {
- if (strpos($columns, '(') !== false) {
- return $select . ' ' . $columns;
- } else {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
- }
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
@@ -720,7 +709,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
- * @param string|array $tables
+ * @param array $tables
* @return string the FROM clause built from [[query]].
*/
public function buildFrom($tables)
@@ -729,13 +718,6 @@ class QueryBuilder extends \yii\base\Object
return '';
}
- if (!is_array($tables)) {
- if (strpos($tables, '(') !== false) {
- return 'FROM ' . $tables;
- } else {
- $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
- }
- }
foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
@@ -756,37 +738,36 @@ class QueryBuilder extends \yii\base\Object
/**
* @param string|array $joins
* @return string the JOIN clause built from [[query]].
+ * @throws Exception if the $joins parameter is not in proper format
*/
public function buildJoin($joins)
{
if (empty($joins)) {
return '';
}
- if (is_string($joins)) {
- return $joins;
- }
foreach ($joins as $i => $join) {
- if (is_array($join)) { // 0:join type, 1:table name, 2:on-condition
- if (isset($join[0], $join[1])) {
- $table = $join[1];
- if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
- $table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
- } else {
- $table = $this->db->quoteTableName($table);
- }
+ if (is_object($join)) {
+ $joins[$i] = (string)$join;
+ } elseif (is_array($join) && isset($join[0], $join[1])) {
+ // 0:join type, 1:table name, 2:on-condition
+ $table = $join[1];
+ if (strpos($table, '(') === false) {
+ if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
+ $table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
+ } else {
+ $table = $this->db->quoteTableName($table);
}
- $joins[$i] = $join[0] . ' ' . $table;
- if (isset($join[2])) {
- $condition = $this->buildCondition($join[2]);
- if ($condition !== '') {
- $joins[$i] .= ' ON ' . $this->buildCondition($join[2]);
- }
+ }
+ $joins[$i] = $join[0] . ' ' . $table;
+ if (isset($join[2])) {
+ $condition = $this->buildCondition($join[2]);
+ if ($condition !== '') {
+ $joins[$i] .= ' ON ' . $this->buildCondition($join[2]);
}
- } else {
- throw new Exception('A join clause must be specified as an array of at least two elements.');
}
+ } else {
+ throw new Exception('A join clause must be specified as an array of join type, join table, and optionally join condition.');
}
}
@@ -804,16 +785,12 @@ class QueryBuilder extends \yii\base\Object
}
/**
- * @param string|array $columns
+ * @param array $columns
* @return string the GROUP BY clause
*/
- public function buildGroup($columns)
+ public function buildGroupBy($columns)
{
- if (empty($columns)) {
- return '';
- } else {
- return 'GROUP BY ' . $this->buildColumns($columns);
- }
+ return empty($columns) ? '' : 'GROUP BY ' . $this->buildColumns($columns);
}
/**
@@ -827,36 +804,24 @@ class QueryBuilder extends \yii\base\Object
}
/**
- * @param string|array $columns
+ * @param array $columns
* @return string the ORDER BY clause built from [[query]].
*/
- public function buildOrder($columns)
+ public function buildOrderBy($columns)
{
if (empty($columns)) {
return '';
}
- if (!is_array($columns)) {
- if (strpos($columns, '(') !== false) {
- return 'ORDER BY ' . $columns;
+ $orders = array();
+ foreach ($columns as $name => $direction) {
+ if (is_object($direction)) {
+ $orders[] = (string)$direction;
} else {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
- }
- foreach ($columns as $i => $column) {
- if (is_object($column)) {
- $columns[$i] = (string)$column;
- } elseif (strpos($column, '(') === false) {
- if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
- $columns[$i] = $this->db->quoteColumnName($matches[1]) . ' ' . $matches[2];
- } else {
- $columns[$i] = $this->db->quoteColumnName($column);
- }
+ $orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : '');
}
}
- if (is_array($columns)) {
- $columns = implode(', ', $columns);
- }
- return 'ORDER BY ' . $columns;
+
+ return 'ORDER BY ' . implode(', ', $orders);
}
/**
@@ -877,7 +842,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
- * @param string|array $unions
+ * @param array $unions
* @return string the UNION clause built from [[query]].
*/
public function buildUnion($unions)
@@ -885,9 +850,6 @@ class QueryBuilder extends \yii\base\Object
if (empty($unions)) {
return '';
}
- if (!is_array($unions)) {
- $unions = array($unions);
- }
foreach ($unions as $i => $union) {
if ($union instanceof Query) {
$unions[$i] = $this->build($union);
diff --git a/framework/db/Schema.php b/framework/db/Schema.php
index 6306776..5fe6121 100644
--- a/framework/db/Schema.php
+++ b/framework/db/Schema.php
@@ -1,9 +1,7 @@
buildKey(__CLASS__, $this->db->dsn, $this->db->username, $name);
+ return $cache->buildKey(array(
+ __CLASS__,
+ $this->db->dsn,
+ $this->db->username,
+ $name,
+ ));
}
/**
diff --git a/framework/db/TableSchema.php b/framework/db/TableSchema.php
index 987d221..1065b51 100644
--- a/framework/db/TableSchema.php
+++ b/framework/db/TableSchema.php
@@ -1,7 +1,5 @@
columns[$key])) {
$this->columns[$key]->isPrimaryKey = true;
} else {
- throw new InvalidCallException("Primary key '$key' cannot be found in table '{$this->name}'.");
+ throw new InvalidParamException("Primary key '$key' cannot be found in table '{$this->name}'.");
}
}
}
diff --git a/framework/db/Transaction.php b/framework/db/Transaction.php
index 3e53c0c..177d2cb 100644
--- a/framework/db/Transaction.php
+++ b/framework/db/Transaction.php
@@ -1,9 +1,7 @@
db->quoteTableName($table);
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow();
if ($row === false) {
- throw new Exception("Unable to find '$oldName' in table '$table'.");
+ throw new Exception("Unable to find column '$oldName' in table '$table'.");
}
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
@@ -98,7 +96,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence
- * @throws InvalidCallException if the table does not exist or there is no sequence associated with the table.
+ * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/
public function resetSequence($tableName, $value = null)
{
@@ -113,9 +111,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
} elseif ($table === null) {
- throw new InvalidCallException("Table not found: $tableName");
+ throw new InvalidParamException("Table not found: $tableName");
} else {
- throw new InvalidCallException("There is not sequence associated with table '$tableName'.'");
+ throw new InvalidParamException("There is not sequence associated with table '$tableName'.'");
}
}
diff --git a/framework/db/mysql/Schema.php b/framework/db/mysql/Schema.php
index 32df0b3..501149a 100644
--- a/framework/db/mysql/Schema.php
+++ b/framework/db/mysql/Schema.php
@@ -1,9 +1,7 @@
firstName . ' ' . $user->lastName;
* });
* ~~~
@@ -242,7 +241,7 @@ class ArrayHelper
* 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 InvalidCallException if the $ascending or $sortFlag parameters do not have
+ * @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)
@@ -255,12 +254,12 @@ class ArrayHelper
if (is_scalar($ascending)) {
$ascending = array_fill(0, $n, $ascending);
} elseif (count($ascending) !== $n) {
- throw new InvalidCallException('The length of $ascending parameter must be the same as that of $keys.');
+ 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 InvalidCallException('The length of $ascending parameter must be the same as that of $keys.');
+ throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.');
}
$args = array();
foreach ($keys as $i => $key) {
@@ -281,4 +280,61 @@ class ArrayHelper
$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;
+ }
}
\ No newline at end of file
diff --git a/framework/util/ConsoleColor.php b/framework/helpers/ConsoleColor.php
similarity index 97%
rename from framework/util/ConsoleColor.php
rename to framework/helpers/ConsoleColor.php
index 1fadc40..429aeb1 100644
--- a/framework/util/ConsoleColor.php
+++ b/framework/helpers/ConsoleColor.php
@@ -1,23 +1,13 @@
$content) {
- if ($name = 'text-decoration') {
+ if ($name === 'text-decoration') {
$content = implode(' ', $content);
}
$styleString[] = $name.':'.$content;
diff --git a/framework/util/FileHelper.php b/framework/helpers/FileHelper.php
similarity index 98%
rename from framework/util/FileHelper.php
rename to framework/helpers/FileHelper.php
index 996fba0..f850b98 100644
--- a/framework/util/FileHelper.php
+++ b/framework/helpers/FileHelper.php
@@ -3,11 +3,11 @@
* Filesystem helper class file.
*
* @link http://www.yiiframework.com/
- * @copyright Copyright © 2008 Yii Software LLC
+ * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
-namespace yii\util;
+namespace yii\helpers;
use yii\base\Exception;
use yii\base\InvalidConfigException;
@@ -43,7 +43,7 @@ class FileHelper
public static function ensureDirectory($path)
{
$p = \Yii::getAlias($path);
- if ($p !== false && ($p = realpath($p)) !== false && is_dir($p)) {
+ if (($p = realpath($p)) !== false && is_dir($p)) {
return $p;
} else {
throw new InvalidConfigException('Directory does not exist: ' . $path);
diff --git a/framework/helpers/Html.php b/framework/helpers/Html.php
new file mode 100644
index 0000000..b004885
--- /dev/null
+++ b/framework/helpers/Html.php
@@ -0,0 +1,976 @@
+
+ * @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 '';
+ }
+
+ /**
+ * 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', "/**/", $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', "/**/", $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 '';
+ }
+
+ /**
+ * Generates a hyperlink tag.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is is coming from end users, you should consider [[encode()]]
+ * it to prevent XSS attacks.
+ * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]]
+ * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
+ * will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated hyperlink
+ * @see url
+ */
+ public static function a($text, $url = null, $options = array())
+ {
+ if ($url !== null) {
+ $options['href'] = static::url($url);
+ }
+ return static::tag('a', $text, $options);
+ }
+
+ /**
+ * Generates a mailto hyperlink.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is is coming from end users, you should consider [[encode()]]
+ * it to prevent XSS attacks.
+ * @param string $email email address. If this is null, the first parameter (link body) will be treated
+ * as the email address and used.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated mailto link
+ */
+ public static function mailto($text, $email = null, $options = array())
+ {
+ return static::a($text, 'mailto:' . ($email === null ? $text : $email), $options);
+ }
+
+ /**
+ * Generates an image tag.
+ * @param string $src the image URL. This parameter will be processed by [[url()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated image tag
+ */
+ public static function img($src, $options = array())
+ {
+ $options['src'] = static::url($src);
+ if (!isset($options['alt'])) {
+ $options['alt'] = '';
+ }
+ return static::tag('img', null, $options);
+ }
+
+ /**
+ * Generates a label tag.
+ * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is is coming from end users, you should consider [[encode()]]
+ * it to prevent XSS attacks.
+ * @param string $for the ID of the HTML element that this label is associated with.
+ * If this is null, the "for" attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated label tag
+ */
+ public static function label($content, $for = null, $options = array())
+ {
+ $options['for'] = $for;
+ return static::tag('label', $content, $options);
+ }
+
+ /**
+ * Generates a button tag.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * If the options does not contain "type", a "type" attribute with value "button" will be rendered.
+ * @return string the generated button tag
+ */
+ public static function button($name = null, $value = null, $content = 'Button', $options = array())
+ {
+ $options['name'] = $name;
+ $options['value'] = $value;
+ if (!isset($options['type'])) {
+ $options['type'] = 'button';
+ }
+ return static::tag('button', $content, $options);
+ }
+
+ /**
+ * Generates a submit button tag.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated submit button tag
+ */
+ public static function submitButton($name = null, $value = null, $content = 'Submit', $options = array())
+ {
+ $options['type'] = 'submit';
+ return static::button($name, $value, $content, $options);
+ }
+
+ /**
+ * Generates a reset button tag.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated reset button tag
+ */
+ public static function resetButton($name = null, $value = null, $content = 'Reset', $options = array())
+ {
+ $options['type'] = 'reset';
+ return static::button($name, $value, $content, $options);
+ }
+
+ /**
+ * Generates an input type of the given type.
+ * @param string $type the type attribute.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated input tag
+ */
+ public static function input($type, $name = null, $value = null, $options = array())
+ {
+ $options['type'] = $type;
+ $options['name'] = $name;
+ $options['value'] = $value;
+ return static::tag('input', null, $options);
+ }
+
+ /**
+ * Generates an input button.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function buttonInput($name, $value = 'Button', $options = array())
+ {
+ return static::input('button', $name, $value, $options);
+ }
+
+ /**
+ * Generates a submit input button.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function submitInput($name = null, $value = 'Submit', $options = array())
+ {
+ return static::input('submit', $name, $value, $options);
+ }
+
+ /**
+ * Generates a reset input button.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
+ * Attributes whose value is null will be ignored and not put in the tag returned.
+ * @return string the generated button tag
+ */
+ public static function resetInput($name = null, $value = 'Reset', $options = array())
+ {
+ return static::input('reset', $name, $value, $options);
+ }
+
+ /**
+ * Generates a text input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function textInput($name, $value = null, $options = array())
+ {
+ return static::input('text', $name, $value, $options);
+ }
+
+ /**
+ * Generates a hidden input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function hiddenInput($name, $value = null, $options = array())
+ {
+ return static::input('hidden', $name, $value, $options);
+ }
+
+ /**
+ * Generates a password input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function passwordInput($name, $value = null, $options = array())
+ {
+ return static::input('password', $name, $value, $options);
+ }
+
+ /**
+ * Generates a file input field.
+ * To use a file input field, you should set the enclosing form's "enctype" attribute to
+ * be "multipart/form-data". After the form is submitted, the uploaded file information
+ * can be obtained via $_FILES[$name] (see PHP documentation).
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function fileInput($name, $value = null, $options = array())
+ {
+ return static::input('file', $name, $value, $options);
+ }
+
+ /**
+ * Generates a text area input.
+ * @param string $name the input name
+ * @param string $value the input value. Note that it will be encoded using [[encode()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated text area tag
+ */
+ public static function textarea($name, $value = '', $options = array())
+ {
+ $options['name'] = $name;
+ return static::tag('textarea', static::encode($value), $options);
+ }
+
+ /**
+ * Generates a radio button input.
+ * @param string $name the name attribute.
+ * @param boolean $checked whether the radio button should be checked.
+ * @param string $value the value attribute. If it is null, the value attribute will not be rendered.
+ * @param array $options the tag options in terms of name-value pairs. The following options are supported:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute
+ * is present, a hidden input will be generated so that if the radio button is not checked and is submitted,
+ * the value of this attribute will still be submitted to the server via the hidden input.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated radio button tag
+ */
+ public static function radio($name, $checked = false, $value = '1', $options = array())
+ {
+ $options['checked'] = $checked;
+ $options['value'] = $value;
+ if (isset($options['uncheck'])) {
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['uncheck']);
+ unset($options['uncheck']);
+ } else {
+ $hidden = '';
+ }
+ return $hidden . static::input('radio', $name, $value, $options);
+ }
+
+ /**
+ * Generates a checkbox input.
+ * @param string $name the name attribute.
+ * @param boolean $checked whether the checkbox should be checked.
+ * @param string $value the value attribute. If it is null, the value attribute will not be rendered.
+ * @param array $options the tag options in terms of name-value pairs. The following options are supported:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
+ * is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
+ * the value of this attribute will still be submitted to the server via the hidden input.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated checkbox tag
+ */
+ public static function checkbox($name, $checked = false, $value = '1', $options = array())
+ {
+ $options['checked'] = $checked;
+ $options['value'] = $value;
+ if (isset($options['uncheck'])) {
+ // add a hidden field so that if the checkbox is not selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['uncheck']);
+ unset($options['uncheck']);
+ } else {
+ $hidden = '';
+ }
+ return $hidden . static::input('checkbox', $name, $value, $options);
+ }
+
+ /**
+ * Generates a drop-down list.
+ * @param string $name the input name
+ * @param string $selection the selected value
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are supported:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * array(
+ * 'value1' => array('disabled' => true),
+ * 'value2' => array('label' => 'value 2'),
+ * );
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated drop-down list tag
+ */
+ public static function dropDownList($name, $selection = null, $items = array(), $options = array())
+ {
+ $options['name'] = $name;
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
+ return static::tag('select', "\n" . $selectOptions . "\n", $options);
+ }
+
+ /**
+ * Generates a list box.
+ * @param string $name the input name
+ * @param string|array $selection the selected value(s)
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are supported:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * array(
+ * 'value1' => array('disabled' => true),
+ * 'value2' => array('label' => 'value 2'),
+ * );
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ * - unselect: string, the value that will be submitted when no option is selected.
+ * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
+ * mode, we can still obtain the posted unselect value.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated list box tag
+ */
+ public static function listBox($name, $selection = null, $items = array(), $options = array())
+ {
+ if (!isset($options['size'])) {
+ $options['size'] = 4;
+ }
+ if (isset($options['multiple']) && $options['multiple'] && substr($name, -2) !== '[]') {
+ $name .= '[]';
+ }
+ $options['name'] = $name;
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ if (substr($name, -2) === '[]') {
+ $name = substr($name, 0, -2);
+ }
+ $hidden = static::hiddenInput($name, $options['unselect']);
+ unset($options['unselect']);
+ } else {
+ $hidden = '';
+ }
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
+ return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
+ }
+
+ /**
+ * Generates a list of checkboxes.
+ * A checkbox list allows multiple selection, like [[listBox()]].
+ * As a result, the corresponding submitted value is an array.
+ * @param string $name the name attribute of each checkbox.
+ * @param string|array $selection the selected value(s).
+ * @param array $items the data item used to generate the checkboxes.
+ * The array keys are the labels, while the array values are the corresponding checkbox values.
+ * Note that the labels will NOT be HTML-encoded, while the values will.
+ * @param array $options options (name => config) for the checkbox list. The following options are supported:
+ *
+ * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
+ * By setting this option, a hidden input will be generated.
+ * - separator: string, the HTML code that separates items.
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the checkbox in the whole list; $label
+ * is the label for the checkbox; and $name, $value and $checked represent the name,
+ * value and the checked status of the checkbox input.
+ * @return string the generated checkbox list
+ */
+ public static function checkboxList($name, $selection = null, $items = array(), $options = array())
+ {
+ if (substr($name, -2) !== '[]') {
+ $name .= '[]';
+ }
+
+ $formatter = isset($options['item']) ? $options['item'] : null;
+ $lines = array();
+ $index = 0;
+ foreach ($items as $value => $label) {
+ $checked = $selection !== null &&
+ (!is_array($selection) && !strcmp($value, $selection)
+ || is_array($selection) && in_array($value, $selection));
+ if ($formatter !== null) {
+ $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
+ } else {
+ $lines[] = static::label(static::checkbox($name, $checked, $value) . ' ' . $label);
+ }
+ $index++;
+ }
+
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
+ $hidden = static::hiddenInput($name2, $options['unselect']);
+ } else {
+ $hidden = '';
+ }
+ $separator = isset($options['separator']) ? $options['separator'] : "\n";
+
+ return $hidden . implode($separator, $lines);
+ }
+
+ /**
+ * Generates a list of radio buttons.
+ * A radio button list is like a checkbox list, except that it only allows single selection.
+ * @param string $name the name attribute of each radio button.
+ * @param string|array $selection the selected value(s).
+ * @param array $items the data item used to generate the radio buttons.
+ * The array keys are the labels, while the array values are the corresponding radio button values.
+ * Note that the labels will NOT be HTML-encoded, while the values will.
+ * @param array $options options (name => config) for the radio button list. The following options are supported:
+ *
+ * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
+ * By setting this option, a hidden input will be generated.
+ * - separator: string, the HTML code that separates items.
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the radio button in the whole list; $label
+ * is the label for the radio button; and $name, $value and $checked represent the name,
+ * value and the checked status of the radio button input.
+ * @return string the generated radio button list
+ */
+ public static function radioList($name, $selection = null, $items = array(), $options = array())
+ {
+ $formatter = isset($options['item']) ? $options['item'] : null;
+ $lines = array();
+ $index = 0;
+ foreach ($items as $value => $label) {
+ $checked = $selection !== null &&
+ (!is_array($selection) && !strcmp($value, $selection)
+ || is_array($selection) && in_array($value, $selection));
+ if ($formatter !== null) {
+ $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
+ } else {
+ $lines[] = static::label(static::radio($name, $checked, $value) . ' ' . $label);
+ }
+ $index++;
+ }
+
+ $separator = isset($options['separator']) ? $options['separator'] : "\n";
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['unselect']);
+ } else {
+ $hidden = '';
+ }
+
+ return $hidden . implode($separator, $lines);
+ }
+
+ /**
+ * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
+ * @param string|array $selection the selected value(s). This can be either a string for single selection
+ * or an array for multiple selections.
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
+ * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
+ * in [[dropDownList()]] for the explanation of these elements.
+ *
+ * @return string the generated list options
+ */
+ public static function renderSelectOptions($selection, $items, &$tagOptions = array())
+ {
+ $lines = array();
+ if (isset($tagOptions['prompt'])) {
+ $prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt']));
+ $lines[] = static::tag('option', $prompt, array('value' => ''));
+ }
+
+ $options = isset($tagOptions['options']) ? $tagOptions['options'] : array();
+ $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array();
+ unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
+
+ foreach ($items as $key => $value) {
+ if (is_array($value)) {
+ $groupAttrs = isset($groups[$key]) ? $groups[$key] : array();
+ $groupAttrs['label'] = $key;
+ $attrs = array('options' => $options, 'groups' => $groups);
+ $content = static::renderSelectOptions($selection, $value, $attrs);
+ $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
+ } else {
+ $attrs = isset($options[$key]) ? $options[$key] : array();
+ $attrs['value'] = $key;
+ $attrs['selected'] = $selection !== null &&
+ (!is_array($selection) && !strcmp($key, $selection)
+ || is_array($selection) && in_array($key, $selection));
+ $lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs);
+ }
+ }
+
+ return implode("\n", $lines);
+ }
+
+ /**
+ * Renders the HTML tag attributes.
+ * Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially
+ * according to [[booleanAttributes]] and [[showBooleanAttributeValues]].
+ * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
+ * Attributes whose value is null will be ignored and not put in the rendering result.
+ * @return string the rendering result. If the attributes are not empty, they will be rendered
+ * into a string with a leading white space (such that it can be directly appended to the tag name
+ * in a tag. If there is no attribute, an empty string will be returned.
+ */
+ public static function renderTagAttributes($attributes)
+ {
+ if (count($attributes) > 1) {
+ $sorted = array();
+ foreach (static::$attributeOrder as $name) {
+ if (isset($attributes[$name])) {
+ $sorted[$name] = $attributes[$name];
+ }
+ }
+ $attributes = array_merge($sorted, $attributes);
+ }
+
+ $html = '';
+ foreach ($attributes as $name => $value) {
+ if (isset(static::$booleanAttributes[strtolower($name)])) {
+ if ($value || strcasecmp($name, $value) === 0) {
+ $html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name";
+ }
+ } elseif ($value !== null) {
+ $html .= " $name=\"" . static::encode($value) . '"';
+ }
+ }
+ return $html;
+ }
+
+ /**
+ * Normalizes the input parameter to be a valid URL.
+ *
+ * If the input parameter
+ *
+ * - is an empty string: the currently requested URL will be returned;
+ * - is a non-empty string: it will be processed by [[Yii::getAlias()]] which, if the string is an alias,
+ * will be resolved into a URL;
+ * - is an array: the first array element is considered a route, while the rest of the name-value
+ * pairs are considered as the parameters to be used for URL creation using [[\yii\base\Application::createUrl()]].
+ * Here are some examples: `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])) {
+ return Yii::$app->createUrl($url[0], array_splice($url, 1));
+ } 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);
+ }
+ }
+}
diff --git a/framework/helpers/SecurityHelper.php b/framework/helpers/SecurityHelper.php
new file mode 100644
index 0000000..5029dd6
--- /dev/null
+++ b/framework/helpers/SecurityHelper.php
@@ -0,0 +1,272 @@
+
+ * @author Tom Worster - * VarDumper::dump($var); - *- * - * @author Qiang Xue