Commit 004a069c by gevik

Merge remote branch 'upstream/master'

parents e45e061c cf1e12ad
...@@ -606,11 +606,36 @@ class YiiBase ...@@ -606,11 +606,36 @@ class YiiBase
public static function t($category, $message, $params = array(), $language = null) public static function t($category, $message, $params = array(), $language = null)
{ {
if (self::$app !== null) { if (self::$app !== null) {
return self::$app->getI18N()->translate($category, $message, $params, $language); return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language);
} else { } else {
return is_array($params) ? strtr($message, $params) : $message; return is_array($params) ? strtr($message, $params) : $message;
} }
} }
/**
* Configures an object with the initial property values.
* @param object $object the object to be configured
* @param array $properties the property initial values given in terms of name-value pairs.
*/
public static function configure($object, $properties)
{
foreach ($properties as $name => $value) {
$object->$name = $value;
}
}
/**
* Returns the public member variables of an object.
* This method is provided such that we can get the public member variables of an object.
* It is different from "get_object_vars()" because the latter will return private
* and protected variables if it is called within the object itself.
* @param object $object the object to be handled
* @return array the public member variables of the object
*/
public static function getObjectVars($object)
{
return get_object_vars($object);
}
} }
YiiBase::$aliases = array( YiiBase::$aliases = array(
......
...@@ -255,7 +255,7 @@ class ErrorHandler extends Component ...@@ -255,7 +255,7 @@ class ErrorHandler extends Component
if (isset($_SERVER['SERVER_SOFTWARE'])) { if (isset($_SERVER['SERVER_SOFTWARE'])) {
foreach ($serverUrls as $url => $keywords) { foreach ($serverUrls as $url => $keywords) {
foreach ($keywords as $keyword) { foreach ($keywords as $keyword) {
if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false ) { if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false) {
return '<a href="' . $url . '" target="_blank">' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '</a>'; return '<a href="' . $url . '" target="_blank">' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '</a>';
} }
} }
......
...@@ -12,7 +12,6 @@ use DateTime; ...@@ -12,7 +12,6 @@ use DateTime;
use yii\helpers\HtmlPurifier; use yii\helpers\HtmlPurifier;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* Formatter provides a set of commonly used data formatting methods. * Formatter provides a set of commonly used data formatting methods.
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace yii\base; namespace yii\base;
/** /**
* HttpException represents an exception caused by an improper request of the end-user. * HttpException represents an exception caused by an improper request of the end-user.
* *
...@@ -42,66 +43,8 @@ class HttpException extends UserException ...@@ -42,66 +43,8 @@ class HttpException extends UserException
*/ */
public function getName() public function getName()
{ {
static $httpCodes = array( if (isset(\yii\web\Response::$statusTexts[$this->statusCode])) {
100 => 'Continue', return \yii\web\Response::$statusTexts[$this->statusCode];
101 => 'Switching Protocols',
102 => 'Processing',
118 => 'Connection timed out',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
210 => 'Content Different',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
310 => 'Too many Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Time-out',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested range unsatisfiable',
417 => 'Expectation failed',
418 => 'I’m a teapot',
422 => 'Unprocessable entity',
423 => 'Locked',
424 => 'Method failure',
425 => 'Unordered Collection',
426 => 'Upgrade Required',
449 => 'Retry With',
450 => 'Blocked by Windows Parental Controls',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway ou Proxy Error',
503 => 'Service Unavailable',
504 => 'Gateway Time-out',
505 => 'HTTP Version not supported',
507 => 'Insufficient storage',
509 => 'Bandwidth Limit Exceeded',
);
if (isset($httpCodes[$this->statusCode])) {
return $httpCodes[$this->statusCode];
} else { } else {
return 'Error'; return 'Error';
} }
......
...@@ -23,4 +23,3 @@ class InvalidCallException extends Exception ...@@ -23,4 +23,3 @@ class InvalidCallException extends Exception
return \Yii::t('yii', 'Invalid Call'); return \Yii::t('yii', 'Invalid Call');
} }
} }
...@@ -23,4 +23,3 @@ class InvalidConfigException extends Exception ...@@ -23,4 +23,3 @@ class InvalidConfigException extends Exception
return \Yii::t('yii', 'Invalid Configuration'); return \Yii::t('yii', 'Invalid Configuration');
} }
} }
...@@ -23,4 +23,3 @@ class InvalidParamException extends Exception ...@@ -23,4 +23,3 @@ class InvalidParamException extends Exception
return \Yii::t('yii', 'Invalid Parameter'); return \Yii::t('yii', 'Invalid Parameter');
} }
} }
...@@ -23,4 +23,3 @@ class InvalidRouteException extends UserException ...@@ -23,4 +23,3 @@ class InvalidRouteException extends UserException
return \Yii::t('yii', 'Invalid Route'); return \Yii::t('yii', 'Invalid Route');
} }
} }
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Jsonable should be implemented by classes that need to be represented in JSON format.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
interface Jsonable
{
/**
* @return string the JSON representation of this object
*/
public function toJson();
}
...@@ -10,6 +10,7 @@ namespace yii\base; ...@@ -10,6 +10,7 @@ namespace yii\base;
use ArrayObject; use ArrayObject;
use ArrayIterator; use ArrayIterator;
use yii\helpers\Inflector; use yii\helpers\Inflector;
use yii\helpers\Json;
use yii\validators\RequiredValidator; use yii\validators\RequiredValidator;
use yii\validators\Validator; use yii\validators\Validator;
...@@ -41,7 +42,7 @@ use yii\validators\Validator; ...@@ -41,7 +42,7 @@ use yii\validators\Validator;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Model extends Component implements \IteratorAggregate, \ArrayAccess class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsonable
{ {
/** /**
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
...@@ -638,6 +639,16 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess ...@@ -638,6 +639,16 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
} }
/** /**
* Returns the JSON representation of this object.
* The default implementation will return [[attributes]].
* @return string the JSON representation of this object.
*/
public function toJson()
{
return Json::encode($this->getAttributes());
}
/**
* Returns an iterator for traversing the attributes in the model. * Returns an iterator for traversing the attributes in the model.
* This method is required by the interface IteratorAggregate. * This method is required by the interface IteratorAggregate.
* @return ArrayIterator an iterator for traversing the items in the list. * @return ArrayIterator an iterator for traversing the items in the list.
......
<?php <?php
/** /**
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii; use Yii;
/** /**
* Module is the base class for module and application classes. * Module is the base class for module and application classes.
* *
* A module represents a sub-application which contains MVC elements by itself, such as * A module represents a sub-application which contains MVC elements by itself, such as
* models, views, controllers, etc. * models, views, controllers, etc.
* *
* A module may consist of [[modules|sub-modules]]. * A module may consist of [[modules|sub-modules]].
* *
* [[components|Components]] may be registered with the module so that they are globally * [[components|Components]] may be registered with the module so that they are globally
* accessible within the module. * accessible within the module.
* *
* @property string $uniqueId An ID that uniquely identifies this module among all modules within * @property string $uniqueId An ID that uniquely identifies this module among all modules within
* the current application. * the current application.
* @property string $basePath The root directory of the module. Defaults to the directory containing the module class. * @property string $basePath The root directory of the module. Defaults to the directory containing the module class.
* @property string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers". * @property string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers".
* @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views". * @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views".
* @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts". * @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts".
* @property array $modules The configuration of the currently installed modules (module ID => configuration). * @property array $modules The configuration of the currently installed modules (module ID => configuration).
* @property array $components The components (indexed by their IDs) registered within this module. * @property array $components The components (indexed by their IDs) registered within this module.
* @property array $import List of aliases to be imported. This property is write-only. * @property array $import List of aliases to be imported. This property is write-only.
* @property array $aliases List of aliases to be defined. This property is write-only. * @property array $aliases List of aliases to be defined. This property is write-only.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
abstract class Module extends Component abstract class Module extends Component
{ {
/** /**
* @event ActionEvent an event raised before executing a controller action. * @event ActionEvent an event raised before executing a controller action.
* You may set [[ActionEvent::isValid]] to be false to cancel the action execution. * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
*/ */
const EVENT_BEFORE_ACTION = 'beforeAction'; const EVENT_BEFORE_ACTION = 'beforeAction';
/** /**
* @event ActionEvent an event raised after executing a controller action. * @event ActionEvent an event raised after executing a controller action.
*/ */
const EVENT_AFTER_ACTION = 'afterAction'; const EVENT_AFTER_ACTION = 'afterAction';
/** /**
* @var array custom module parameters (name => value). * @var array custom module parameters (name => value).
*/ */
public $params = array(); public $params = array();
/** /**
* @var array the IDs of the components that should be preloaded when this module is created. * @var array the IDs of the components that should be preloaded when this module is created.
*/ */
public $preload = array(); public $preload = array();
/** /**
* @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]]. * @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].
*/ */
public $id; public $id;
/** /**
* @var Module the parent module of this module. Null if this module does not have a parent. * @var Module the parent module of this module. Null if this module does not have a parent.
*/ */
public $module; public $module;
/** /**
* @var string|boolean the layout that should be applied for views within this module. This refers to a view name * @var string|boolean the layout that should be applied for views within this module. This refers to a view name
* relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]] * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]
* will be taken. If this is false, layout will be disabled within this module. * will be taken. If this is false, layout will be disabled within this module.
*/ */
public $layout; public $layout;
/** /**
* @var array mapping from controller ID to controller configurations. * @var array mapping from controller ID to controller configurations.
* Each name-value pair specifies the configuration of a single controller. * Each name-value pair specifies the configuration of a single controller.
* A controller configuration can be either a string or an array. * A controller configuration can be either a string or an array.
* If the former, the string should be the class name or path alias of the controller. * If the former, the string should be the class name or path alias of the controller.
* If the latter, the array must contain a 'class' element which specifies * If the latter, the array must contain a 'class' element which specifies
* the controller's class name or path alias, and the rest of the name-value pairs * the controller's class name or path alias, and the rest of the name-value pairs
* in the array are used to initialize the corresponding controller properties. For example, * in the array are used to initialize the corresponding controller properties. For example,
* *
* ~~~ * ~~~
* array( * array(
* 'account' => '@app/controllers/UserController', * 'account' => '@app/controllers/UserController',
* 'article' => array( * 'article' => array(
* 'class' => '@app/controllers/PostController', * 'class' => '@app/controllers/PostController',
* 'pageTitle' => 'something new', * 'pageTitle' => 'something new',
* ), * ),
* ) * )
* ~~~ * ~~~
*/ */
public $controllerMap = array(); public $controllerMap = array();
/** /**
* @var string the namespace that controller classes are in. If not set, * @var string the namespace that controller classes are in. If not set,
* it will use the "controllers" sub-namespace under the namespace of this module. * it will use the "controllers" sub-namespace under the namespace of this module.
* For example, if the namespace of this module is "foo\bar", then the default * For example, if the namespace of this module is "foo\bar", then the default
* controller namespace would be "foo\bar\controllers". * controller namespace would be "foo\bar\controllers".
* If the module is an application, it will default to "app\controllers". * If the module is an application, it will default to "app\controllers".
*/ */
public $controllerNamespace; public $controllerNamespace;
/** /**
* @return string the default route of this module. Defaults to 'default'. * @return string the default route of this module. Defaults to 'default'.
* The route may consist of child module ID, controller ID, and/or action ID. * The route may consist of child module ID, controller ID, and/or action ID.
* For example, `help`, `post/create`, `admin/post/create`. * For example, `help`, `post/create`, `admin/post/create`.
* If action ID is not given, it will take the default value as specified in * If action ID is not given, it will take the default value as specified in
* [[Controller::defaultAction]]. * [[Controller::defaultAction]].
*/ */
public $defaultRoute = 'default'; public $defaultRoute = 'default';
/** /**
* @var string the root directory of the module. * @var string the root directory of the module.
*/ */
private $_basePath; private $_basePath;
/** /**
* @var string the root directory that contains view files for this module * @var string the root directory that contains view files for this module
*/ */
private $_viewPath; private $_viewPath;
/** /**
* @var string the root directory that contains layout view files for this module. * @var string the root directory that contains layout view files for this module.
*/ */
private $_layoutPath; private $_layoutPath;
/** /**
* @var string the directory containing controller classes in the module. * @var string the directory containing controller classes in the module.
*/ */
private $_controllerPath; private $_controllerPath;
/** /**
* @var array child modules of this module * @var array child modules of this module
*/ */
private $_modules = array(); private $_modules = array();
/** /**
* @var array components registered under this module * @var array components registered under this module
*/ */
private $_components = array(); private $_components = array();
/** /**
* Constructor. * Constructor.
* @param string $id the ID of this module * @param string $id the ID of this module
* @param Module $parent the parent module (if any) * @param Module $parent the parent module (if any)
* @param array $config name-value pairs that will be used to initialize the object properties * @param array $config name-value pairs that will be used to initialize the object properties
*/ */
public function __construct($id, $parent = null, $config = array()) public function __construct($id, $parent = null, $config = array())
{ {
$this->id = $id; $this->id = $id;
$this->module = $parent; $this->module = $parent;
parent::__construct($config); parent::__construct($config);
} }
/** /**
* Getter magic method. * Getter magic method.
* This method is overridden to support accessing components * This method is overridden to support accessing components
* like reading module properties. * like reading module properties.
* @param string $name component or property name * @param string $name component or property name
* @return mixed the named property value * @return mixed the named property value
*/ */
public function __get($name) public function __get($name)
{ {
if ($this->hasComponent($name)) { if ($this->hasComponent($name)) {
return $this->getComponent($name); return $this->getComponent($name);
} else { } else {
return parent::__get($name); return parent::__get($name);
} }
} }
/** /**
* Checks if a property value is null. * Checks if a property value is null.
* This method overrides the parent implementation by checking * This method overrides the parent implementation by checking
* if the named component is loaded. * if the named component is loaded.
* @param string $name the property name or the event name * @param string $name the property name or the event name
* @return boolean whether the property value is null * @return boolean whether the property value is null
*/ */
public function __isset($name) public function __isset($name)
{ {
if ($this->hasComponent($name)) { if ($this->hasComponent($name)) {
return $this->getComponent($name) !== null; return $this->getComponent($name) !== null;
} else { } else {
return parent::__isset($name); return parent::__isset($name);
} }
} }
/** /**
* Initializes the module. * Initializes the module.
* This method is called after the module is created and initialized with property values * This method is called after the module is created and initialized with property values
* given in configuration. The default implement will create a path alias using the module [[id]] * given in configuration. The default implement will create a path alias using the module [[id]]
* and then call [[preloadComponents()]] to load components that are declared in [[preload]]. * and then call [[preloadComponents()]] to load components that are declared in [[preload]].
*/ */
public function init() public function init()
{ {
$this->preloadComponents(); $this->preloadComponents();
if ($this->controllerNamespace === null) { if ($this->controllerNamespace === null) {
if ($this instanceof Application) { if ($this instanceof Application) {
$this->controllerNamespace = 'app\\controllers'; $this->controllerNamespace = 'app\\controllers';
} else { } else {
$class = get_class($this); $class = get_class($this);
if (($pos = strrpos($class, '\\')) !== false) { if (($pos = strrpos($class, '\\')) !== false) {
$this->controllerNamespace = substr($class, 0, $pos) . '\\controllers'; $this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
} }
} }
} }
} }
/** /**
* Returns an ID that uniquely identifies this module among all modules within the current application. * Returns an ID that uniquely identifies this module among all modules within the current application.
* Note that if the module is an application, an empty string will be returned. * Note that if the module is an application, an empty string will be returned.
* @return string the unique ID of the module. * @return string the unique ID of the module.
*/ */
public function getUniqueId() public function getUniqueId()
{ {
if ($this instanceof Application) { if ($this instanceof Application) {
return ''; return '';
} elseif ($this->module) { } elseif ($this->module) {
return $this->module->getUniqueId() . '/' . $this->id; return $this->module->getUniqueId() . '/' . $this->id;
} else { } else {
return $this->id; return $this->id;
} }
} }
/** /**
* Returns the root directory of the module. * Returns the root directory of the module.
* It defaults to the directory containing the module class file. * It defaults to the directory containing the module class file.
* @return string the root directory of the module. * @return string the root directory of the module.
*/ */
public function getBasePath() public function getBasePath()
{ {
if ($this->_basePath === null) { if ($this->_basePath === null) {
$class = new \ReflectionClass($this); $class = new \ReflectionClass($this);
$this->_basePath = dirname($class->getFileName()); $this->_basePath = dirname($class->getFileName());
} }
return $this->_basePath; return $this->_basePath;
} }
/** /**
* Sets the root directory of the module. * Sets the root directory of the module.
* This method can only be invoked at the beginning of the constructor. * This method can only be invoked at the beginning of the constructor.
* @param string $path the root directory of the module. This can be either a directory name or a path alias. * @param string $path the root directory of the module. This can be either a directory name or a path alias.
* @throws InvalidParamException if the directory does not exist. * @throws InvalidParamException if the directory does not exist.
*/ */
public function setBasePath($path) public function setBasePath($path)
{ {
$path = Yii::getAlias($path); $path = Yii::getAlias($path);
$p = realpath($path); $p = realpath($path);
if ($p !== false && is_dir($p)) { if ($p !== false && is_dir($p)) {
$this->_basePath = $p; $this->_basePath = $p;
if ($this instanceof Application) { if ($this instanceof Application) {
Yii::setAlias('@app', $p); Yii::setAlias('@app', $p);
} }
} else { } else {
throw new InvalidParamException("The directory does not exist: $path"); throw new InvalidParamException("The directory does not exist: $path");
} }
} }
/** /**
* Returns the directory that contains the controller classes. * Returns the directory that contains the controller classes.
* Defaults to "[[basePath]]/controllers". * Defaults to "[[basePath]]/controllers".
* @return string the directory that contains the controller classes. * @return string the directory that contains the controller classes.
*/ */
public function getControllerPath() public function getControllerPath()
{ {
if ($this->_controllerPath !== null) { if ($this->_controllerPath !== null) {
return $this->_controllerPath; return $this->_controllerPath;
} else { } else {
return $this->_controllerPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'controllers'; return $this->_controllerPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'controllers';
} }
} }
/** /**
* Sets the directory that contains the controller classes. * Sets the directory that contains the controller classes.
* @param string $path the directory that contains the controller classes. * @param string $path the directory that contains the controller classes.
* This can be either a directory name or a path alias. * This can be either a directory name or a path alias.
* @throws Exception if the directory is invalid * @throws Exception if the directory is invalid
*/ */
public function setControllerPath($path) public function setControllerPath($path)
{ {
$this->_controllerPath = Yii::getAlias($path); $this->_controllerPath = Yii::getAlias($path);
} }
/** /**
* Returns the directory that contains the view files for this module. * Returns the directory that contains the view files for this module.
* @return string the root directory of view files. Defaults to "[[basePath]]/view". * @return string the root directory of view files. Defaults to "[[basePath]]/view".
*/ */
public function getViewPath() public function getViewPath()
{ {
if ($this->_viewPath !== null) { if ($this->_viewPath !== null) {
return $this->_viewPath; return $this->_viewPath;
} else { } else {
return $this->_viewPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'views'; return $this->_viewPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'views';
} }
} }
/** /**
* Sets the directory that contains the view files. * Sets the directory that contains the view files.
* @param string $path the root directory of view files. * @param string $path the root directory of view files.
* @throws Exception if the directory is invalid * @throws Exception if the directory is invalid
*/ */
public function setViewPath($path) public function setViewPath($path)
{ {
$this->_viewPath = Yii::getAlias($path); $this->_viewPath = Yii::getAlias($path);
} }
/** /**
* Returns the directory that contains layout view files for this module. * Returns the directory that contains layout view files for this module.
* @return string the root directory of layout files. Defaults to "[[viewPath]]/layouts". * @return string the root directory of layout files. Defaults to "[[viewPath]]/layouts".
*/ */
public function getLayoutPath() public function getLayoutPath()
{ {
if ($this->_layoutPath !== null) { if ($this->_layoutPath !== null) {
return $this->_layoutPath; return $this->_layoutPath;
} else { } else {
return $this->_layoutPath = $this->getViewPath() . DIRECTORY_SEPARATOR . 'layouts'; return $this->_layoutPath = $this->getViewPath() . DIRECTORY_SEPARATOR . 'layouts';
} }
} }
/** /**
* Sets the directory that contains the layout files. * Sets the directory that contains the layout files.
* @param string $path the root directory of layout files. * @param string $path the root directory of layout files.
* @throws Exception if the directory is invalid * @throws Exception if the directory is invalid
*/ */
public function setLayoutPath($path) public function setLayoutPath($path)
{ {
$this->_layoutPath = Yii::getAlias($path); $this->_layoutPath = Yii::getAlias($path);
} }
/** /**
* Defines path aliases. * Defines path aliases.
* This method calls [[Yii::setAlias()]] to register the path aliases. * This method calls [[Yii::setAlias()]] to register the path aliases.
* This method is provided so that you can define path aliases when configuring a module. * This method is provided so that you can define path aliases when configuring a module.
* @param array $aliases list of path aliases to be defined. The array keys are alias names * @param array $aliases list of path aliases to be defined. The array keys are alias names
* (must start with '@') and the array values are the corresponding paths or aliases. * (must start with '@') and the array values are the corresponding paths or aliases.
* For example, * For example,
* *
* ~~~ * ~~~
* array( * array(
* '@models' => '@app/models', // an existing alias * '@models' => '@app/models', // an existing alias
* '@backend' => __DIR__ . '/../backend', // a directory * '@backend' => __DIR__ . '/../backend', // a directory
* ) * )
* ~~~ * ~~~
*/ */
public function setAliases($aliases) public function setAliases($aliases)
{ {
foreach ($aliases as $name => $alias) { foreach ($aliases as $name => $alias) {
Yii::setAlias($name, $alias); Yii::setAlias($name, $alias);
} }
} }
/** /**
* Checks whether the named module exists. * Checks whether the named module exists.
* @param string $id module ID * @param string $id module ID
* @return boolean whether the named module exists. Both loaded and unloaded modules * @return boolean whether the named module exists. Both loaded and unloaded modules
* are considered. * are considered.
*/ */
public function hasModule($id) public function hasModule($id)
{ {
return isset($this->_modules[$id]); return isset($this->_modules[$id]);
} }
/** /**
* Retrieves the named module. * Retrieves the named module.
* @param string $id module ID (case-sensitive) * @param string $id module ID (case-sensitive)
* @param boolean $load whether to load the module if it is not yet loaded. * @param boolean $load whether to load the module if it is not yet loaded.
* @return Module|null the module instance, null if the module * @return Module|null the module instance, null if the module
* does not exist. * does not exist.
* @see hasModule() * @see hasModule()
*/ */
public function getModule($id, $load = true) public function getModule($id, $load = true)
{ {
if (isset($this->_modules[$id])) { if (isset($this->_modules[$id])) {
if ($this->_modules[$id] instanceof Module) { if ($this->_modules[$id] instanceof Module) {
return $this->_modules[$id]; return $this->_modules[$id];
} elseif ($load) { } elseif ($load) {
Yii::trace("Loading module: $id", __METHOD__); Yii::trace("Loading module: $id", __METHOD__);
return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this); return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this);
} }
} }
return null; return null;
} }
/** /**
* Adds a sub-module to this module. * Adds a sub-module to this module.
* @param string $id module ID * @param string $id module ID
* @param Module|array|null $module the sub-module to be added to this module. This can * @param Module|array|null $module the sub-module to be added to this module. This can
* be one of the followings: * be one of the followings:
* *
* - a [[Module]] object * - a [[Module]] object
* - a configuration array: when [[getModule()]] is called initially, the array * - a configuration array: when [[getModule()]] is called initially, the array
* will be used to instantiate the sub-module * will be used to instantiate the sub-module
* - null: the named sub-module will be removed from this module * - null: the named sub-module will be removed from this module
*/ */
public function setModule($id, $module) public function setModule($id, $module)
{ {
if ($module === null) { if ($module === null) {
unset($this->_modules[$id]); unset($this->_modules[$id]);
} else { } else {
$this->_modules[$id] = $module; $this->_modules[$id] = $module;
} }
} }
/** /**
* Returns the sub-modules in this module. * Returns the sub-modules in this module.
* @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set false, * @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set false,
* then all sub-modules registered in this module will be returned, whether they are loaded or not. * then all sub-modules registered in this module will be returned, whether they are loaded or not.
* Loaded modules will be returned as objects, while unloaded modules as configuration arrays. * Loaded modules will be returned as objects, while unloaded modules as configuration arrays.
* @return array the modules (indexed by their IDs) * @return array the modules (indexed by their IDs)
*/ */
public function getModules($loadedOnly = false) public function getModules($loadedOnly = false)
{ {
if ($loadedOnly) { if ($loadedOnly) {
$modules = array(); $modules = array();
foreach ($this->_modules as $module) { foreach ($this->_modules as $module) {
if ($module instanceof Module) { if ($module instanceof Module) {
$modules[] = $module; $modules[] = $module;
} }
} }
return $modules; return $modules;
} else { } else {
return $this->_modules; return $this->_modules;
} }
} }
/** /**
* Registers sub-modules in the current module. * Registers sub-modules in the current module.
* *
* Each sub-module should be specified as a name-value pair, where * Each sub-module should be specified as a name-value pair, where
* name refers to the ID of the module and value the module or a configuration * name refers to the ID of the module and value the module or a configuration
* array that can be used to create the module. In the latter case, [[Yii::createObject()]] * array that can be used to create the module. In the latter case, [[Yii::createObject()]]
* will be used to create the module. * will be used to create the module.
* *
* If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently. * If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently.
* *
* The following is an example for registering two sub-modules: * The following is an example for registering two sub-modules:
* *
* ~~~ * ~~~
* array( * array(
* 'comment' => array( * 'comment' => array(
* 'class' => 'app\modules\comment\CommentModule', * 'class' => 'app\modules\comment\CommentModule',
* 'db' => 'db', * 'db' => 'db',
* ), * ),
* 'booking' => array( * 'booking' => array(
* 'class' => 'app\modules\booking\BookingModule', * 'class' => 'app\modules\booking\BookingModule',
* ), * ),
* ) * )
* ~~~ * ~~~
* *
* @param array $modules modules (id => module configuration or instances) * @param array $modules modules (id => module configuration or instances)
*/ */
public function setModules($modules) public function setModules($modules)
{ {
foreach ($modules as $id => $module) { foreach ($modules as $id => $module) {
$this->_modules[$id] = $module; $this->_modules[$id] = $module;
} }
} }
/** /**
* Checks whether the named component exists. * Checks whether the named component exists.
* @param string $id component ID * @param string $id component ID
* @return boolean whether the named component exists. Both loaded and unloaded components * @return boolean whether the named component exists. Both loaded and unloaded components
* are considered. * are considered.
*/ */
public function hasComponent($id) public function hasComponent($id)
{ {
return isset($this->_components[$id]); return isset($this->_components[$id]);
} }
/** /**
* Retrieves the named component. * Retrieves the named component.
* @param string $id component ID (case-sensitive) * @param string $id component ID (case-sensitive)
* @param boolean $load whether to load the component if it is not yet loaded. * @param boolean $load whether to load the component if it is not yet loaded.
* @return Component|null the component instance, null if the component does not exist. * @return Component|null the component instance, null if the component does not exist.
* @see hasComponent() * @see hasComponent()
*/ */
public function getComponent($id, $load = true) public function getComponent($id, $load = true)
{ {
if (isset($this->_components[$id])) { if (isset($this->_components[$id])) {
if ($this->_components[$id] instanceof Object) { if ($this->_components[$id] instanceof Object) {
return $this->_components[$id]; return $this->_components[$id];
} elseif ($load) { } elseif ($load) {
Yii::trace("Loading component: $id", __METHOD__); Yii::trace("Loading component: $id", __METHOD__);
return $this->_components[$id] = Yii::createObject($this->_components[$id]); return $this->_components[$id] = Yii::createObject($this->_components[$id]);
} }
} }
return null; return null;
} }
/** /**
* Registers a component with this module. * Registers a component with this module.
* @param string $id component ID * @param string $id component ID
* @param Component|array|null $component the component to be registered with the module. This can * @param Component|array|null $component the component to be registered with the module. This can
* be one of the followings: * be one of the followings:
* *
* - a [[Component]] object * - a [[Component]] object
* - a configuration array: when [[getComponent()]] is called initially for this component, the array * - a configuration array: when [[getComponent()]] is called initially for this component, the array
* will be used to instantiate the component via [[Yii::createObject()]]. * will be used to instantiate the component via [[Yii::createObject()]].
* - null: the named component will be removed from the module * - null: the named component will be removed from the module
*/ */
public function setComponent($id, $component) public function setComponent($id, $component)
{ {
if ($component === null) { if ($component === null) {
unset($this->_components[$id]); unset($this->_components[$id]);
} else { } else {
$this->_components[$id] = $component; $this->_components[$id] = $component;
} }
} }
/** /**
* Returns the registered components. * Returns the registered components.
* @param boolean $loadedOnly whether to return the loaded components only. If this is set false, * @param boolean $loadedOnly whether to return the loaded components only. If this is set false,
* then all components specified in the configuration will be returned, whether they are loaded or not. * then all components specified in the configuration will be returned, whether they are loaded or not.
* Loaded components will be returned as objects, while unloaded components as configuration arrays. * Loaded components will be returned as objects, while unloaded components as configuration arrays.
* @return array the components (indexed by their IDs) * @return array the components (indexed by their IDs)
*/ */
public function getComponents($loadedOnly = false) public function getComponents($loadedOnly = false)
{ {
if ($loadedOnly) { if ($loadedOnly) {
$components = array(); $components = array();
foreach ($this->_components as $component) { foreach ($this->_components as $component) {
if ($component instanceof Component) { if ($component instanceof Component) {
$components[] = $component; $components[] = $component;
} }
} }
return $components; return $components;
} else { } else {
return $this->_components; return $this->_components;
} }
} }
/** /**
* Registers a set of components in this module. * Registers a set of components in this module.
* *
* Each component should be specified as a name-value pair, where * Each component should be specified as a name-value pair, where
* name refers to the ID of the component and value the component or a configuration * name refers to the ID of the component and value the component or a configuration
* array that can be used to create the component. In the latter case, [[Yii::createObject()]] * array that can be used to create the component. In the latter case, [[Yii::createObject()]]
* will be used to create the component. * will be used to create the component.
* *
* If a new component has the same ID as an existing one, the existing one will be overwritten silently. * If a new component has the same ID as an existing one, the existing one will be overwritten silently.
* *
* The following is an example for setting two components: * The following is an example for setting two components:
* *
* ~~~ * ~~~
* array( * array(
* 'db' => array( * 'db' => array(
* 'class' => 'yii\db\Connection', * 'class' => 'yii\db\Connection',
* 'dsn' => 'sqlite:path/to/file.db', * 'dsn' => 'sqlite:path/to/file.db',
* ), * ),
* 'cache' => array( * 'cache' => array(
* 'class' => 'yii\caching\DbCache', * 'class' => 'yii\caching\DbCache',
* 'db' => 'db', * 'db' => 'db',
* ), * ),
* ) * )
* ~~~ * ~~~
* *
* @param array $components components (id => component configuration or instance) * @param array $components components (id => component configuration or instance)
*/ */
public function setComponents($components) public function setComponents($components)
{ {
foreach ($components as $id => $component) { foreach ($components as $id => $component) {
if (isset($this->_components[$id]['class']) && !isset($component['class'])) { if (isset($this->_components[$id]['class']) && !isset($component['class'])) {
$component['class'] = $this->_components[$id]['class']; $component['class'] = $this->_components[$id]['class'];
} }
$this->_components[$id] = $component; $this->_components[$id] = $component;
} }
} }
/** /**
* Loads components that are declared in [[preload]]. * Loads components that are declared in [[preload]].
*/ */
public function preloadComponents() public function preloadComponents()
{ {
foreach ($this->preload as $id) { foreach ($this->preload as $id) {
$this->getComponent($id); $this->getComponent($id);
} }
} }
/** /**
* Runs a controller action specified by a route. * Runs a controller action specified by a route.
* This method parses the specified route and creates the corresponding child module(s), controller and action * This method parses the specified route and creates the corresponding child module(s), controller and action
* instances. It then calls [[Controller::runAction()]] to run the action with the given parameters. * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
* If the route is empty, the method will use [[defaultRoute]]. * If the route is empty, the method will use [[defaultRoute]].
* @param string $route the route that specifies the action. * @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action * @param array $params the parameters to be passed to the action
* @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal. * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully * @throws InvalidRouteException if the requested route cannot be resolved into an action successfully
*/ */
public function runAction($route, $params = array()) public function runAction($route, $params = array())
{ {
$result = $this->createController($route); $result = $this->createController($route);
if (is_array($result)) { if (is_array($result)) {
/** @var $controller Controller */ /** @var $controller Controller */
list($controller, $actionID) = $result; list($controller, $actionID) = $result;
$oldController = Yii::$app->controller; $oldController = Yii::$app->controller;
Yii::$app->controller = $controller; Yii::$app->controller = $controller;
$status = $controller->runAction($actionID, $params); $status = $controller->runAction($actionID, $params);
Yii::$app->controller = $oldController; Yii::$app->controller = $oldController;
return $status; return $status;
} else { } else {
throw new InvalidRouteException('Unable to resolve the request "' . trim($this->getUniqueId() . '/' . $route, '/') . '".'); throw new InvalidRouteException('Unable to resolve the request "' . trim($this->getUniqueId() . '/' . $route, '/') . '".');
} }
} }
/** /**
* Creates a controller instance based on the controller ID. * Creates a controller instance based on the controller ID.
* *
* The controller is created within this module. The method first attempts to * The controller is created within this module. The method first attempts to
* create the controller based on the [[controllerMap]] of the module. If not available, * create the controller based on the [[controllerMap]] of the module. If not available,
* it will look for the controller class under the [[controllerPath]] and create an * it will look for the controller class under the [[controllerPath]] and create an
* instance of it. * instance of it.
* *
* @param string $route the route consisting of module, controller and action IDs. * @param string $route the route consisting of module, controller and action IDs.
* @return array|boolean If the controller is created successfully, it will be returned together * @return array|boolean If the controller is created successfully, it will be returned together
* with the requested action ID. Otherwise false will be returned. * with the requested action ID. Otherwise false will be returned.
* @throws InvalidConfigException if the controller class and its file do not match. * @throws InvalidConfigException if the controller class and its file do not match.
*/ */
public function createController($route) public function createController($route)
{ {
if ($route === '') { if ($route === '') {
$route = $this->defaultRoute; $route = $this->defaultRoute;
} }
$route = trim($route, '/'); $route = trim($route, '/');
if (($pos = strpos($route, '/')) !== false) { if (($pos = strpos($route, '/')) !== false) {
$id = substr($route, 0, $pos); $id = substr($route, 0, $pos);
$route = substr($route, $pos + 1); $route = substr($route, $pos + 1);
} else { } else {
$id = $route; $id = $route;
$route = ''; $route = '';
} }
$module = $this->getModule($id); $module = $this->getModule($id);
if ($module !== null) { if ($module !== null) {
return $module->createController($route); return $module->createController($route);
} }
if (isset($this->controllerMap[$id])) { if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], $id, $this); $controller = Yii::createObject($this->controllerMap[$id], $id, $this);
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
$className = str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))) . 'Controller'; $className = str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))) . 'Controller';
$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php'; $classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) { if (!is_file($classFile)) {
return false; return false;
} }
$className = ltrim($this->controllerNamespace . '\\' . $className, '\\'); $className = ltrim($this->controllerNamespace . '\\' . $className, '\\');
Yii::$classMap[$className] = $classFile; Yii::$classMap[$className] = $classFile;
if (is_subclass_of($className, 'yii\base\Controller')) { if (is_subclass_of($className, 'yii\base\Controller')) {
$controller = new $className($id, $this); $controller = new $className($id, $this);
} elseif (YII_DEBUG) { } elseif (YII_DEBUG) {
throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller."); throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
} }
} }
return isset($controller) ? array($controller, $route) : false; return isset($controller) ? array($controller, $route) : false;
} }
/** /**
* This method is invoked right before an action is to be executed (after all possible filters.) * This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action. * You may override this method to do last-minute preparation for the action.
* @param Action $action the action to be executed. * @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed. * @return boolean whether the action should continue to be executed.
*/ */
public function beforeAction($action) public function beforeAction($action)
{ {
$event = new ActionEvent($action); $event = new ActionEvent($action);
$this->trigger(self::EVENT_BEFORE_ACTION, $event); $this->trigger(self::EVENT_BEFORE_ACTION, $event);
return $event->isValid; return $event->isValid;
} }
/** /**
* This method is invoked right after an action is executed. * This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action. * You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed. * @param Action $action the action just executed.
*/ */
public function afterAction($action) public function afterAction($action)
{ {
$this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action)); $this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action));
} }
} }
...@@ -23,4 +23,3 @@ class NotSupportedException extends Exception ...@@ -23,4 +23,3 @@ class NotSupportedException extends Exception
return \Yii::t('yii', 'Not Supported'); return \Yii::t('yii', 'Not Supported');
} }
} }
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
namespace yii\base; namespace yii\base;
use Yii;
use yii\helpers\Json;
/** /**
* @include @yii/base/Object.md * @include @yii/base/Object.md
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Object class Object implements Jsonable
{ {
/** /**
* @return string the fully qualified name of this class. * @return string the fully qualified name of this class.
...@@ -38,8 +41,8 @@ class Object ...@@ -38,8 +41,8 @@ class Object
*/ */
public function __construct($config = array()) public function __construct($config = array())
{ {
foreach ($config as $name => $value) { if (!empty($config)) {
$this->$name = $value; Yii::configure($this, $config);
} }
$this->init(); $this->init();
} }
...@@ -216,4 +219,14 @@ class Object ...@@ -216,4 +219,14 @@ class Object
{ {
return method_exists($this, 'set' . $name) || $checkVar && property_exists($this, $name); return method_exists($this, 'set' . $name) || $checkVar && property_exists($this, $name);
} }
/**
* Returns the JSON representation of this object.
* The default implementation will return all public member variables.
* @return string the JSON representation of this object.
*/
public function toJson()
{
return Json::encode(Yii::getObjectVars($this));
}
} }
...@@ -73,7 +73,7 @@ class Theme extends Component ...@@ -73,7 +73,7 @@ class Theme extends Component
*/ */
public function init() public function init()
{ {
parent::init(); parent::init();
if (empty($this->pathMap)) { if (empty($this->pathMap)) {
if ($this->basePath !== null) { if ($this->basePath !== null) {
$this->basePath = Yii::getAlias($this->basePath); $this->basePath = Yii::getAlias($this->basePath);
......
...@@ -23,4 +23,3 @@ class UnknownClassException extends Exception ...@@ -23,4 +23,3 @@ class UnknownClassException extends Exception
return \Yii::t('yii', 'Unknown Class'); return \Yii::t('yii', 'Unknown Class');
} }
} }
...@@ -23,4 +23,3 @@ class UnknownMethodException extends Exception ...@@ -23,4 +23,3 @@ class UnknownMethodException extends Exception
return \Yii::t('yii', 'Unknown Method'); return \Yii::t('yii', 'Unknown Method');
} }
} }
...@@ -23,4 +23,3 @@ class UnknownPropertyException extends Exception ...@@ -23,4 +23,3 @@ class UnknownPropertyException extends Exception
return \Yii::t('yii', 'Unknown Property'); return \Yii::t('yii', 'Unknown Property');
} }
} }
...@@ -6,9 +6,8 @@ ...@@ -6,9 +6,8 @@
*/ */
namespace yii\bootstrap; namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\helpers\Html;
/** /**
* Button renders a bootstrap button. * Button renders a bootstrap button.
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
*/ */
namespace yii\bootstrap; namespace yii\bootstrap;
use yii\helpers\Html;
use yii\helpers\Html;
/** /**
* ButtonDropdown renders a group or split button dropdown bootstrap component. * ButtonDropdown renders a group or split button dropdown bootstrap component.
......
...@@ -10,7 +10,6 @@ namespace yii\bootstrap; ...@@ -10,7 +10,6 @@ namespace yii\bootstrap;
use yii\helpers\base\ArrayHelper; use yii\helpers\base\ArrayHelper;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* ButtonGroup renders a button group bootstrap component. * ButtonGroup renders a button group bootstrap component.
* *
......
...@@ -130,4 +130,4 @@ class Collapse extends Widget ...@@ -130,4 +130,4 @@ class Collapse extends Widget
return implode("\n", $group); return implode("\n", $group);
} }
} }
\ No newline at end of file
...@@ -11,7 +11,6 @@ use yii\base\InvalidConfigException; ...@@ -11,7 +11,6 @@ use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* Dropdown renders a Bootstrap dropdown menu component. * Dropdown renders a Bootstrap dropdown menu component.
* *
......
...@@ -118,7 +118,7 @@ class Nav extends Widget ...@@ -118,7 +118,7 @@ class Nav extends Widget
$url = Html::url(ArrayHelper::getValue($item, 'url', '#')); $url = Html::url(ArrayHelper::getValue($item, 'url', '#'));
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', array()); $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array());
if(ArrayHelper::getValue($item, 'active')) { if (ArrayHelper::getValue($item, 'active')) {
$this->addCssClass($options, 'active'); $this->addCssClass($options, 'active');
} }
......
...@@ -11,7 +11,6 @@ use yii\base\InvalidConfigException; ...@@ -11,7 +11,6 @@ use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* Progress renders a bootstrap progress bar component. * Progress renders a bootstrap progress bar component.
* *
......
...@@ -11,7 +11,6 @@ use Yii; ...@@ -11,7 +11,6 @@ use Yii;
use yii\base\View; use yii\base\View;
use yii\helpers\Json; use yii\helpers\Json;
/** /**
* \yii\bootstrap\Widget is the base class for all bootstrap widgets. * \yii\bootstrap\Widget is the base class for all bootstrap widgets.
* *
......
...@@ -170,7 +170,7 @@ class DbCache extends Cache ...@@ -170,7 +170,7 @@ class DbCache extends Cache
} else { } else {
return $this->addValue($key, $value, $expire); return $this->addValue($key, $value, $expire);
} }
} }
/** /**
* Stores a value identified by a key into cache if the cache does not contain this key. * Stores a value identified by a key into cache if the cache does not contain this key.
......
...@@ -86,4 +86,3 @@ class XCache extends Cache ...@@ -86,4 +86,3 @@ class XCache extends Cache
return true; return true;
} }
} }
...@@ -25,4 +25,3 @@ class Exception extends UserException ...@@ -25,4 +25,3 @@ class Exception extends UserException
return \Yii::t('yii', 'Error'); return \Yii::t('yii', 'Error');
} }
} }
...@@ -164,7 +164,6 @@ class AssetController extends Controller ...@@ -164,7 +164,6 @@ class AssetController extends Controller
protected function loadConfiguration($configFile) protected function loadConfiguration($configFile)
{ {
echo "Loading configuration from '{$configFile}'...\n"; echo "Loading configuration from '{$configFile}'...\n";
foreach (require($configFile) as $name => $value) { foreach (require($configFile) as $name => $value) {
if (property_exists($this, $name) || $this->canSetProperty($name)) { if (property_exists($this, $name) || $this->canSetProperty($name)) {
$this->$name = $value; $this->$name = $value;
...@@ -221,7 +220,8 @@ class AssetController extends Controller ...@@ -221,7 +220,8 @@ class AssetController extends Controller
* @param array $result already loaded bundles list. * @param array $result already loaded bundles list.
* @throws \yii\console\Exception on failure. * @throws \yii\console\Exception on failure.
*/ */
protected function loadBundleDependency($name, $bundle, &$result) { protected function loadBundleDependency($name, $bundle, &$result)
{
if (!empty($bundle->depends)) { if (!empty($bundle->depends)) {
$assetManager = $this->getAssetManager(); $assetManager = $this->getAssetManager();
foreach ($bundle->depends as $dependencyName) { foreach ($bundle->depends as $dependencyName) {
...@@ -573,7 +573,7 @@ EOD; ...@@ -573,7 +573,7 @@ EOD;
$inputFileRelativePathParts = explode('/', $inputFileRelativePath); $inputFileRelativePathParts = explode('/', $inputFileRelativePath);
$outputFileRelativePathParts = explode('/', $outputFileRelativePath); $outputFileRelativePathParts = explode('/', $outputFileRelativePath);
$callback = function($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) { $callback = function ($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) {
$fullMatch = $matches[0]; $fullMatch = $matches[0];
$inputUrl = $matches[1]; $inputUrl = $matches[1];
......
...@@ -179,8 +179,7 @@ class MessageController extends Controller ...@@ -179,8 +179,7 @@ class MessageController extends Controller
} }
ksort($translated); ksort($translated);
foreach ($translated as $message => $translation) { foreach ($translated as $message => $translation) {
if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld) if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld) {
{
if (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') { if (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') {
$todo[$message]=$translation; $todo[$message]=$translation;
} else { } else {
......
<?php <?php
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\console\controllers; namespace yii\console\controllers;
use Yii; use Yii;
use yii\console\Exception; use yii\console\Exception;
use yii\console\Controller; use yii\console\Controller;
use yii\db\Connection; use yii\db\Connection;
use yii\db\Query; use yii\db\Query;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
/** /**
* This command manages application migrations. * This command manages application migrations.
* *
* A migration means a set of persistent changes to the application environment * A migration means a set of persistent changes to the application environment
* that is shared among different developers. For example, in an application * that is shared among different developers. For example, in an application
* backed by a database, a migration may refer to a set of changes to * backed by a database, a migration may refer to a set of changes to
* the database, such as creating a new table, adding a new table column. * the database, such as creating a new table, adding a new table column.
* *
* This command provides support for tracking the migration history, upgrading * This command provides support for tracking the migration history, upgrading
* or downloading with migrations, and creating new migration skeletons. * or downloading with migrations, and creating new migration skeletons.
* *
* The migration history is stored in a database table named * The migration history is stored in a database table named
* as [[migrationTable]]. The table will be automatically created the first time * as [[migrationTable]]. The table will be automatically created the first time
* this command is executed, if it does not exist. You may also manually * this command is executed, if it does not exist. You may also manually
* create it as follows: * create it as follows:
* *
* ~~~ * ~~~
* CREATE TABLE tbl_migration ( * CREATE TABLE tbl_migration (
* version varchar(255) PRIMARY KEY, * version varchar(255) PRIMARY KEY,
* apply_time integer * apply_time integer
* ) * )
* ~~~ * ~~~
* *
* Below are some common usages of this command: * Below are some common usages of this command:
* *
* ~~~ * ~~~
* # creates a new migration named 'create_user_table' * # creates a new migration named 'create_user_table'
* yii migrate/create create_user_table * yii migrate/create create_user_table
* *
* # applies ALL new migrations * # applies ALL new migrations
* yii migrate * yii migrate
* *
* # reverts the last applied migration * # reverts the last applied migration
* yii migrate/down * yii migrate/down
* ~~~ * ~~~
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class MigrateController extends Controller class MigrateController extends Controller
{ {
/** /**
* The name of the dummy migration that marks the beginning of the whole migration history. * The name of the dummy migration that marks the beginning of the whole migration history.
*/ */
const BASE_MIGRATION = 'm000000_000000_base'; const BASE_MIGRATION = 'm000000_000000_base';
/** /**
* @var string the default command action. * @var string the default command action.
*/ */
public $defaultAction = 'up'; public $defaultAction = 'up';
/** /**
* @var string the directory storing the migration classes. This can be either * @var string the directory storing the migration classes. This can be either
* a path alias or a directory. * a path alias or a directory.
*/ */
public $migrationPath = '@app/migrations'; public $migrationPath = '@app/migrations';
/** /**
* @var string the name of the table for keeping applied migration information. * @var string the name of the table for keeping applied migration information.
*/ */
public $migrationTable = 'tbl_migration'; public $migrationTable = 'tbl_migration';
/** /**
* @var string the template file for generating new migrations. * @var string the template file for generating new migrations.
* This can be either a path alias (e.g. "@app/migrations/template.php") * This can be either a path alias (e.g. "@app/migrations/template.php")
* or a file path. * or a file path.
*/ */
public $templateFile = '@yii/views/migration.php'; public $templateFile = '@yii/views/migration.php';
/** /**
* @var boolean whether to execute the migration in an interactive mode. * @var boolean whether to execute the migration in an interactive mode.
*/ */
public $interactive = true; public $interactive = true;
/** /**
* @var Connection|string the DB connection object or the application * @var Connection|string the DB connection object or the application
* component ID of the DB connection. * component ID of the DB connection.
*/ */
public $db = 'db'; public $db = 'db';
/** /**
* Returns the names of the global options for this command. * Returns the names of the global options for this command.
* @return array the names of the global options for this command. * @return array the names of the global options for this command.
*/ */
public function globalOptions() public function globalOptions()
{ {
return array('migrationPath', 'migrationTable', 'db', 'templateFile', 'interactive'); return array('migrationPath', 'migrationTable', 'db', 'templateFile', 'interactive');
} }
/** /**
* This method is invoked right before an action is to be executed (after all possible filters.) * This method is invoked right before an action is to be executed (after all possible filters.)
* It checks the existence of the [[migrationPath]]. * It checks the existence of the [[migrationPath]].
* @param \yii\base\Action $action the action to be executed. * @param \yii\base\Action $action the action to be executed.
* @return boolean whether the action should continue to be executed. * @return boolean whether the action should continue to be executed.
* @throws Exception if the migration directory does not exist. * @throws Exception if the migration directory does not exist.
*/ */
public function beforeAction($action) public function beforeAction($action)
{ {
if (parent::beforeAction($action)) { if (parent::beforeAction($action)) {
$path = Yii::getAlias($this->migrationPath); $path = Yii::getAlias($this->migrationPath);
if (!is_dir($path)) { if (!is_dir($path)) {
throw new Exception("The migration directory \"{$this->migrationPath}\" does not exist."); throw new Exception("The migration directory \"{$this->migrationPath}\" does not exist.");
} }
$this->migrationPath = $path; $this->migrationPath = $path;
if($action->id!=='create') { if ($action->id !== 'create') {
if (is_string($this->db)) { if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db); $this->db = Yii::$app->getComponent($this->db);
} }
if (!$this->db instanceof Connection) { if (!$this->db instanceof Connection) {
throw new Exception("The 'db' option must refer to the application component ID of a DB connection."); throw new Exception("The 'db' option must refer to the application component ID of a DB connection.");
} }
} }
$version = Yii::getVersion(); $version = Yii::getVersion();
echo "Yii Migration Tool (based on Yii v{$version})\n\n"; echo "Yii Migration Tool (based on Yii v{$version})\n\n";
return true; return true;
} else { } else {
return false; return false;
} }
} }
/** /**
* Upgrades the application by applying new migrations. * Upgrades the application by applying new migrations.
* For example, * For example,
* *
* ~~~ * ~~~
* yii migrate # apply all new migrations * yii migrate # apply all new migrations
* yii migrate 3 # apply the first 3 new migrations * yii migrate 3 # apply the first 3 new migrations
* ~~~ * ~~~
* *
* @param integer $limit the number of new migrations to be applied. If 0, it means * @param integer $limit the number of new migrations to be applied. If 0, it means
* applying all available new migrations. * applying all available new migrations.
*/ */
public function actionUp($limit = 0) public function actionUp($limit = 0)
{ {
$migrations = $this->getNewMigrations(); $migrations = $this->getNewMigrations();
if (empty($migrations)) { if (empty($migrations)) {
echo "No new migration found. Your system is up-to-date.\n"; echo "No new migration found. Your system is up-to-date.\n";
Yii::$app->end(); Yii::$app->end();
} }
$total = count($migrations); $total = count($migrations);
$limit = (int)$limit; $limit = (int)$limit;
if ($limit > 0) { if ($limit > 0) {
$migrations = array_slice($migrations, 0, $limit); $migrations = array_slice($migrations, 0, $limit);
} }
$n = count($migrations); $n = count($migrations);
if ($n === $total) { if ($n === $total) {
echo "Total $n new " . ($n === 1 ? 'migration' : 'migrations') . " to be applied:\n"; echo "Total $n new " . ($n === 1 ? 'migration' : 'migrations') . " to be applied:\n";
} else { } else {
echo "Total $n out of $total new " . ($total === 1 ? 'migration' : 'migrations') . " to be applied:\n"; echo "Total $n out of $total new " . ($total === 1 ? 'migration' : 'migrations') . " to be applied:\n";
} }
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
echo " $migration\n"; echo " $migration\n";
} }
echo "\n"; echo "\n";
if ($this->confirm('Apply the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) { if ($this->confirm('Apply the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
if (!$this->migrateUp($migration)) { if (!$this->migrateUp($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return;
} }
} }
echo "\nMigrated up successfully.\n"; echo "\nMigrated up successfully.\n";
} }
} }
/** /**
* Downgrades the application by reverting old migrations. * Downgrades the application by reverting old migrations.
* For example, * For example,
* *
* ~~~ * ~~~
* yii migrate/down # revert the last migration * yii migrate/down # revert the last migration
* yii migrate/down 3 # revert the last 3 migrations * yii migrate/down 3 # revert the last 3 migrations
* ~~~ * ~~~
* *
* @param integer $limit the number of migrations to be reverted. Defaults to 1, * @param integer $limit the number of migrations to be reverted. Defaults to 1,
* meaning the last applied migration will be reverted. * meaning the last applied migration will be reverted.
* @throws Exception if the number of the steps specified is less than 1. * @throws Exception if the number of the steps specified is less than 1.
*/ */
public function actionDown($limit = 1) public function actionDown($limit = 1)
{ {
$limit = (int)$limit; $limit = (int)$limit;
if ($limit < 1) { if ($limit < 1) {
throw new Exception("The step argument must be greater than 0."); throw new Exception("The step argument must be greater than 0.");
} }
$migrations = $this->getMigrationHistory($limit); $migrations = $this->getMigrationHistory($limit);
if (empty($migrations)) { if (empty($migrations)) {
echo "No migration has been done before.\n"; echo "No migration has been done before.\n";
return; return;
} }
$migrations = array_keys($migrations); $migrations = array_keys($migrations);
$n = count($migrations); $n = count($migrations);
echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be reverted:\n"; echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be reverted:\n";
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
echo " $migration\n"; echo " $migration\n";
} }
echo "\n"; echo "\n";
if ($this->confirm('Revert the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) { if ($this->confirm('Revert the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
if (!$this->migrateDown($migration)) { if (!$this->migrateDown($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return;
} }
} }
echo "\nMigrated down successfully.\n"; echo "\nMigrated down successfully.\n";
} }
} }
/** /**
* Redoes the last few migrations. * Redoes the last few migrations.
* *
* This command will first revert the specified migrations, and then apply * This command will first revert the specified migrations, and then apply
* them again. For example, * them again. For example,
* *
* ~~~ * ~~~
* yii migrate/redo # redo the last applied migration * yii migrate/redo # redo the last applied migration
* yii migrate/redo 3 # redo the last 3 applied migrations * yii migrate/redo 3 # redo the last 3 applied migrations
* ~~~ * ~~~
* *
* @param integer $limit the number of migrations to be redone. Defaults to 1, * @param integer $limit the number of migrations to be redone. Defaults to 1,
* meaning the last applied migration will be redone. * meaning the last applied migration will be redone.
* @throws Exception if the number of the steps specified is less than 1. * @throws Exception if the number of the steps specified is less than 1.
*/ */
public function actionRedo($limit = 1) public function actionRedo($limit = 1)
{ {
$limit = (int)$limit; $limit = (int)$limit;
if ($limit < 1) { if ($limit < 1) {
throw new Exception("The step argument must be greater than 0."); throw new Exception("The step argument must be greater than 0.");
} }
$migrations = $this->getMigrationHistory($limit); $migrations = $this->getMigrationHistory($limit);
if (empty($migrations)) { if (empty($migrations)) {
echo "No migration has been done before.\n"; echo "No migration has been done before.\n";
return; return;
} }
$migrations = array_keys($migrations); $migrations = array_keys($migrations);
$n = count($migrations); $n = count($migrations);
echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be redone:\n"; echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be redone:\n";
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
echo " $migration\n"; echo " $migration\n";
} }
echo "\n"; echo "\n";
if ($this->confirm('Redo the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) { if ($this->confirm('Redo the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
if (!$this->migrateDown($migration)) { if (!$this->migrateDown($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return;
} }
} }
foreach (array_reverse($migrations) as $migration) { foreach (array_reverse($migrations) as $migration) {
if (!$this->migrateUp($migration)) { if (!$this->migrateUp($migration)) {
echo "\nMigration failed. The rest of the migrations migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations migrations are canceled.\n";
return; return;
} }
} }
echo "\nMigration redone successfully.\n"; echo "\nMigration redone successfully.\n";
} }
} }
/** /**
* Upgrades or downgrades till the specified version. * Upgrades or downgrades till the specified version.
* *
* This command will first revert the specified migrations, and then apply * This command will first revert the specified migrations, and then apply
* them again. For example, * them again. For example,
* *
* ~~~ * ~~~
* yii migrate/to 101129_185401 # using timestamp * yii migrate/to 101129_185401 # using timestamp
* yii migrate/to m101129_185401_create_user_table # using full name * yii migrate/to m101129_185401_create_user_table # using full name
* ~~~ * ~~~
* *
* @param string $version the version name that the application should be migrated to. * @param string $version the version name that the application should be migrated to.
* This can be either the timestamp or the full name of the migration. * This can be either the timestamp or the full name of the migration.
* @throws Exception if the version argument is invalid * @throws Exception if the version argument is invalid
*/ */
public function actionTo($version) public function actionTo($version)
{ {
$originalVersion = $version; $originalVersion = $version;
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) { if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
$version = 'm' . $matches[1]; $version = 'm' . $matches[1];
} else { } else {
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table)."); throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
} }
// try migrate up // try migrate up
$migrations = $this->getNewMigrations(); $migrations = $this->getNewMigrations();
foreach ($migrations as $i => $migration) { foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) { if (strpos($migration, $version . '_') === 0) {
$this->actionUp($i + 1); $this->actionUp($i + 1);
return; return;
} }
} }
// try migrate down // try migrate down
$migrations = array_keys($this->getMigrationHistory(-1)); $migrations = array_keys($this->getMigrationHistory(-1));
foreach ($migrations as $i => $migration) { foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) { if (strpos($migration, $version . '_') === 0) {
if ($i === 0) { if ($i === 0) {
echo "Already at '$originalVersion'. Nothing needs to be done.\n"; echo "Already at '$originalVersion'. Nothing needs to be done.\n";
} else { } else {
$this->actionDown($i); $this->actionDown($i);
} }
return; return;
} }
} }
throw new Exception("Unable to find the version '$originalVersion'."); throw new Exception("Unable to find the version '$originalVersion'.");
} }
/** /**
* Modifies the migration history to the specified version. * Modifies the migration history to the specified version.
* *
* No actual migration will be performed. * No actual migration will be performed.
* *
* ~~~ * ~~~
* yii migrate/mark 101129_185401 # using timestamp * yii migrate/mark 101129_185401 # using timestamp
* yii migrate/mark m101129_185401_create_user_table # using full name * yii migrate/mark m101129_185401_create_user_table # using full name
* ~~~ * ~~~
* *
* @param string $version the version at which the migration history should be marked. * @param string $version the version at which the migration history should be marked.
* This can be either the timestamp or the full name of the migration. * This can be either the timestamp or the full name of the migration.
* @throws Exception if the version argument is invalid or the version cannot be found. * @throws Exception if the version argument is invalid or the version cannot be found.
*/ */
public function actionMark($version) public function actionMark($version)
{ {
$originalVersion = $version; $originalVersion = $version;
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) { if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
$version = 'm' . $matches[1]; $version = 'm' . $matches[1];
} else { } else {
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table)."); throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
} }
// try mark up // try mark up
$migrations = $this->getNewMigrations(); $migrations = $this->getNewMigrations();
foreach ($migrations as $i => $migration) { foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) { if (strpos($migration, $version . '_') === 0) {
if ($this->confirm("Set migration history at $originalVersion?")) { if ($this->confirm("Set migration history at $originalVersion?")) {
$command = $this->db->createCommand(); $command = $this->db->createCommand();
for ($j = 0; $j <= $i; ++$j) { for ($j = 0; $j <= $i; ++$j) {
$command->insert($this->migrationTable, array( $command->insert($this->migrationTable, array(
'version' => $migrations[$j], 'version' => $migrations[$j],
'apply_time' => time(), 'apply_time' => time(),
))->execute(); ))->execute();
} }
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
} }
return; return;
} }
} }
// try mark down // try mark down
$migrations = array_keys($this->getMigrationHistory(-1)); $migrations = array_keys($this->getMigrationHistory(-1));
foreach ($migrations as $i => $migration) { foreach ($migrations as $i => $migration) {
if (strpos($migration, $version . '_') === 0) { if (strpos($migration, $version . '_') === 0) {
if ($i === 0) { if ($i === 0) {
echo "Already at '$originalVersion'. Nothing needs to be done.\n"; echo "Already at '$originalVersion'. Nothing needs to be done.\n";
} else { } else {
if ($this->confirm("Set migration history at $originalVersion?")) { if ($this->confirm("Set migration history at $originalVersion?")) {
$command = $this->db->createCommand(); $command = $this->db->createCommand();
for ($j = 0; $j < $i; ++$j) { for ($j = 0; $j < $i; ++$j) {
$command->delete($this->migrationTable, array( $command->delete($this->migrationTable, array(
'version' => $migrations[$j], 'version' => $migrations[$j],
))->execute(); ))->execute();
} }
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
} }
} }
return; return;
} }
} }
throw new Exception("Unable to find the version '$originalVersion'."); throw new Exception("Unable to find the version '$originalVersion'.");
} }
/** /**
* Displays the migration history. * Displays the migration history.
* *
* This command will show the list of migrations that have been applied * This command will show the list of migrations that have been applied
* so far. For example, * so far. For example,
* *
* ~~~ * ~~~
* yii migrate/history # showing the last 10 migrations * yii migrate/history # showing the last 10 migrations
* yii migrate/history 5 # showing the last 5 migrations * yii migrate/history 5 # showing the last 5 migrations
* yii migrate/history 0 # showing the whole history * yii migrate/history 0 # showing the whole history
* ~~~ * ~~~
* *
* @param integer $limit the maximum number of migrations to be displayed. * @param integer $limit the maximum number of migrations to be displayed.
* If it is 0, the whole migration history will be displayed. * If it is 0, the whole migration history will be displayed.
*/ */
public function actionHistory($limit = 10) public function actionHistory($limit = 10)
{ {
$limit = (int)$limit; $limit = (int)$limit;
$migrations = $this->getMigrationHistory($limit); $migrations = $this->getMigrationHistory($limit);
if (empty($migrations)) { if (empty($migrations)) {
echo "No migration has been done before.\n"; echo "No migration has been done before.\n";
} else { } else {
$n = count($migrations); $n = count($migrations);
if ($limit > 0) { if ($limit > 0) {
echo "Showing the last $n applied " . ($n === 1 ? 'migration' : 'migrations') . ":\n"; echo "Showing the last $n applied " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
} else { } else {
echo "Total $n " . ($n === 1 ? 'migration has' : 'migrations have') . " been applied before:\n"; echo "Total $n " . ($n === 1 ? 'migration has' : 'migrations have') . " been applied before:\n";
} }
foreach ($migrations as $version => $time) { foreach ($migrations as $version => $time) {
echo " (" . date('Y-m-d H:i:s', $time) . ') ' . $version . "\n"; echo " (" . date('Y-m-d H:i:s', $time) . ') ' . $version . "\n";
} }
} }
} }
/** /**
* Displays the un-applied new migrations. * Displays the un-applied new migrations.
* *
* This command will show the new migrations that have not been applied. * This command will show the new migrations that have not been applied.
* For example, * For example,
* *
* ~~~ * ~~~
* yii migrate/new # showing the first 10 new migrations * yii migrate/new # showing the first 10 new migrations
* yii migrate/new 5 # showing the first 5 new migrations * yii migrate/new 5 # showing the first 5 new migrations
* yii migrate/new 0 # showing all new migrations * yii migrate/new 0 # showing all new migrations
* ~~~ * ~~~
* *
* @param integer $limit the maximum number of new migrations to be displayed. * @param integer $limit the maximum number of new migrations to be displayed.
* If it is 0, all available new migrations will be displayed. * If it is 0, all available new migrations will be displayed.
*/ */
public function actionNew($limit = 10) public function actionNew($limit = 10)
{ {
$limit = (int)$limit; $limit = (int)$limit;
$migrations = $this->getNewMigrations(); $migrations = $this->getNewMigrations();
if (empty($migrations)) { if (empty($migrations)) {
echo "No new migrations found. Your system is up-to-date.\n"; echo "No new migrations found. Your system is up-to-date.\n";
} else { } else {
$n = count($migrations); $n = count($migrations);
if ($limit > 0 && $n > $limit) { if ($limit > 0 && $n > $limit) {
$migrations = array_slice($migrations, 0, $limit); $migrations = array_slice($migrations, 0, $limit);
echo "Showing $limit out of $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n"; echo "Showing $limit out of $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
} else { } else {
echo "Found $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n"; echo "Found $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
} }
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
echo " " . $migration . "\n"; echo " " . $migration . "\n";
} }
} }
} }
/** /**
* Creates a new migration. * Creates a new migration.
* *
* This command creates a new migration using the available migration template. * This command creates a new migration using the available migration template.
* After using this command, developers should modify the created migration * After using this command, developers should modify the created migration
* skeleton by filling up the actual migration logic. * skeleton by filling up the actual migration logic.
* *
* ~~~ * ~~~
* yii migrate/create create_user_table * yii migrate/create create_user_table
* ~~~ * ~~~
* *
* @param string $name the name of the new migration. This should only contain * @param string $name the name of the new migration. This should only contain
* letters, digits and/or underscores. * letters, digits and/or underscores.
* @throws Exception if the name argument is invalid. * @throws Exception if the name argument is invalid.
*/ */
public function actionCreate($name) public function actionCreate($name)
{ {
if (!preg_match('/^\w+$/', $name)) { if (!preg_match('/^\w+$/', $name)) {
throw new Exception("The migration name should contain letters, digits and/or underscore characters only."); throw new Exception("The migration name should contain letters, digits and/or underscore characters only.");
} }
$name = 'm' . gmdate('ymd_His') . '_' . $name; $name = 'm' . gmdate('ymd_His') . '_' . $name;
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $name . '.php'; $file = $this->migrationPath . DIRECTORY_SEPARATOR . $name . '.php';
if ($this->confirm("Create new migration '$file'?")) { if ($this->confirm("Create new migration '$file'?")) {
$content = $this->renderFile(Yii::getAlias($this->templateFile), array( $content = $this->renderFile(Yii::getAlias($this->templateFile), array(
'className' => $name, 'className' => $name,
)); ));
file_put_contents($file, $content); file_put_contents($file, $content);
echo "New migration created successfully.\n"; echo "New migration created successfully.\n";
} }
} }
/** /**
* Upgrades with the specified migration class. * Upgrades with the specified migration class.
* @param string $class the migration class name * @param string $class the migration class name
* @return boolean whether the migration is successful * @return boolean whether the migration is successful
*/ */
protected function migrateUp($class) protected function migrateUp($class)
{ {
if ($class === self::BASE_MIGRATION) { if ($class === self::BASE_MIGRATION) {
return true; return true;
} }
echo "*** applying $class\n"; echo "*** applying $class\n";
$start = microtime(true); $start = microtime(true);
$migration = $this->createMigration($class); $migration = $this->createMigration($class);
if ($migration->up() !== false) { if ($migration->up() !== false) {
$this->db->createCommand()->insert($this->migrationTable, array( $this->db->createCommand()->insert($this->migrationTable, array(
'version' => $class, 'version' => $class,
'apply_time' => time(), 'apply_time' => time(),
))->execute(); ))->execute();
$time = microtime(true) - $start; $time = microtime(true) - $start;
echo "*** applied $class (time: " . sprintf("%.3f", $time) . "s)\n\n"; echo "*** applied $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return true; return true;
} else { } else {
$time = microtime(true) - $start; $time = microtime(true) - $start;
echo "*** failed to apply $class (time: " . sprintf("%.3f", $time) . "s)\n\n"; echo "*** failed to apply $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return false; return false;
} }
} }
/** /**
* Downgrades with the specified migration class. * Downgrades with the specified migration class.
* @param string $class the migration class name * @param string $class the migration class name
* @return boolean whether the migration is successful * @return boolean whether the migration is successful
*/ */
protected function migrateDown($class) protected function migrateDown($class)
{ {
if ($class === self::BASE_MIGRATION) { if ($class === self::BASE_MIGRATION) {
return true; return true;
} }
echo "*** reverting $class\n"; echo "*** reverting $class\n";
$start = microtime(true); $start = microtime(true);
$migration = $this->createMigration($class); $migration = $this->createMigration($class);
if ($migration->down() !== false) { if ($migration->down() !== false) {
$this->db->createCommand()->delete($this->migrationTable, array( $this->db->createCommand()->delete($this->migrationTable, array(
'version' => $class, 'version' => $class,
))->execute(); ))->execute();
$time = microtime(true) - $start; $time = microtime(true) - $start;
echo "*** reverted $class (time: " . sprintf("%.3f", $time) . "s)\n\n"; echo "*** reverted $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return true; return true;
} else { } else {
$time = microtime(true) - $start; $time = microtime(true) - $start;
echo "*** failed to revert $class (time: " . sprintf("%.3f", $time) . "s)\n\n"; echo "*** failed to revert $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return false; return false;
} }
} }
/** /**
* Creates a new migration instance. * Creates a new migration instance.
* @param string $class the migration class name * @param string $class the migration class name
* @return \yii\db\Migration the migration instance * @return \yii\db\Migration the migration instance
*/ */
protected function createMigration($class) protected function createMigration($class)
{ {
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php'; $file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
require_once($file); require_once($file);
return new $class(array( return new $class(array(
'db' => $this->db, 'db' => $this->db,
)); ));
} }
/** /**
* Returns the migration history. * Returns the migration history.
* @param integer $limit the maximum number of records in the history to be returned * @param integer $limit the maximum number of records in the history to be returned
* @return array the migration history * @return array the migration history
*/ */
protected function getMigrationHistory($limit) protected function getMigrationHistory($limit)
{ {
if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) { if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) {
$this->createMigrationHistoryTable(); $this->createMigrationHistoryTable();
} }
$query = new Query; $query = new Query;
$rows = $query->select(array('version', 'apply_time')) $rows = $query->select(array('version', 'apply_time'))
->from($this->migrationTable) ->from($this->migrationTable)
->orderBy('version DESC') ->orderBy('version DESC')
->limit($limit) ->limit($limit)
->createCommand() ->createCommand()
->queryAll(); ->queryAll();
$history = ArrayHelper::map($rows, 'version', 'apply_time'); $history = ArrayHelper::map($rows, 'version', 'apply_time');
unset($history[self::BASE_MIGRATION]); unset($history[self::BASE_MIGRATION]);
return $history; return $history;
} }
/** /**
* Creates the migration history table. * Creates the migration history table.
*/ */
protected function createMigrationHistoryTable() protected function createMigrationHistoryTable()
{ {
echo 'Creating migration history table "' . $this->migrationTable . '"...'; echo 'Creating migration history table "' . $this->migrationTable . '"...';
$this->db->createCommand()->createTable($this->migrationTable, array( $this->db->createCommand()->createTable($this->migrationTable, array(
'version' => 'varchar(255) NOT NULL PRIMARY KEY', 'version' => 'varchar(255) NOT NULL PRIMARY KEY',
'apply_time' => 'integer', 'apply_time' => 'integer',
))->execute(); ))->execute();
$this->db->createCommand()->insert($this->migrationTable, array( $this->db->createCommand()->insert($this->migrationTable, array(
'version' => self::BASE_MIGRATION, 'version' => self::BASE_MIGRATION,
'apply_time' => time(), 'apply_time' => time(),
))->execute(); ))->execute();
echo "done.\n"; echo "done.\n";
} }
/** /**
* Returns the migrations that are not applied. * Returns the migrations that are not applied.
* @return array list of new migrations * @return array list of new migrations
*/ */
protected function getNewMigrations() protected function getNewMigrations()
{ {
$applied = array(); $applied = array();
foreach ($this->getMigrationHistory(-1) as $version => $time) { foreach ($this->getMigrationHistory(-1) as $version => $time) {
$applied[substr($version, 1, 13)] = true; $applied[substr($version, 1, 13)] = true;
} }
$migrations = array(); $migrations = array();
$handle = opendir($this->migrationPath); $handle = opendir($this->migrationPath);
while (($file = readdir($handle)) !== false) { while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') { if ($file === '.' || $file === '..') {
continue; continue;
} }
$path = $this->migrationPath . DIRECTORY_SEPARATOR . $file; $path = $this->migrationPath . DIRECTORY_SEPARATOR . $file;
if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && is_file($path) && !isset($applied[$matches[2]])) { if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && is_file($path) && !isset($applied[$matches[2]])) {
$migrations[] = $matches[1]; $migrations[] = $matches[1];
} }
} }
closedir($handle); closedir($handle);
sort($migrations); sort($migrations);
return $migrations; return $migrations;
} }
} }
...@@ -305,8 +305,7 @@ class Connection extends Component ...@@ -305,8 +305,7 @@ class Connection extends Component
$this->pdo = $this->createPdoInstance(); $this->pdo = $this->createPdoInstance();
$this->initConnection(); $this->initConnection();
Yii::endProfile($token, __METHOD__); Yii::endProfile($token, __METHOD__);
} } catch (\PDOException $e) {
catch (\PDOException $e) {
Yii::endProfile($token, __METHOD__); Yii::endProfile($token, __METHOD__);
Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__); Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__);
$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.'; $message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
...@@ -343,8 +342,6 @@ class Connection extends Component ...@@ -343,8 +342,6 @@ class Connection extends Component
$driver = strtolower(substr($this->dsn, 0, $pos)); $driver = strtolower(substr($this->dsn, 0, $pos));
if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') { if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') {
$pdoClass = 'yii\db\mssql\PDO'; $pdoClass = 'yii\db\mssql\PDO';
} else if ($driver === 'pgsql') {
$pdoClass = 'yii\db\pgsql\PDO';
} }
} }
return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes); return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes);
...@@ -510,7 +507,7 @@ class Connection extends Component ...@@ -510,7 +507,7 @@ class Connection extends Component
{ {
$db = $this; $db = $this;
return preg_replace_callback('/(\\{\\{([%\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/', return preg_replace_callback('/(\\{\\{([%\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
function($matches) use($db) { function ($matches) use ($db) {
if (isset($matches[3])) { if (isset($matches[3])) {
return $db->quoteColumnName($matches[3]); return $db->quoteColumnName($matches[3]);
} else { } else {
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\pgsql;
/**
* This is an extension of the default PDO class for PostgreSQL drivers.
* It provides additional low level functionality for setting database
* configuration parameters.
*
* @author Gevik babakhani <gevikb@gmail.com>
* @since 2.0
*/
class PDO extends \PDO
{
const OPT_SEARCH_PATH = 'search_path';
const OPT_DEFAULT_SCHEMA = 'default_schema';
const DEFAULT_SCHEMA = 'public';
private $_currentDatabase;
/**
* Returns value of the last inserted ID.
* @param string|null $sequence the sequence name. Defaults to null.
* @return integer last inserted ID value.
*/
public function lastInsertId($sequence = null) {
if ($sequence !== null) {
$sequence = $this->quote($sequence);
return $this->query("SELECT currval({$sequence})")->fetchColumn();
} else {
return null;
}
}
/**
* Here we override the default PDO constructor in order to
* find and set the default schema search path.
*/
public function __construct($dsn, $username, $passwd, $options) {
$searchPath = null;
if (is_array($options)) {
if (isset($options[self::OPT_SEARCH_PATH])) {
$matches = null;
if (preg_match("/(\s?)+(\w)+((\s+)?,(\s+)?\w+)*/", $options[self::OPT_SEARCH_PATH], $matches) === 1) {
$searchPath = $matches[0];
}
}
if (isset($options[self::OPT_DEFAULT_SCHEMA])) {
$schema = trim($options[self::OPT_DEFAULT_SCHEMA]);
if (!empty($schema)) {
Schema::$DEFAULT_SCHEMA = $schema;
}
}
if (is_null(Schema::$DEFAULT_SCHEMA) || empty(Schema::$DEFAULT_SCHEMA)) {
Schema::$DEFAULT_SCHEMA = self::DEFAULT_SCHEMA;
}
}
parent::__construct($dsn, $username, $passwd, $options);
if (!is_null($searchPath)) {
$this->setSchemaSearchPath($searchPath);
}
}
/**
* Returns the name of the current (connected) database
* @return string
*/
public function getCurrentDatabase() {
if (is_null($this->_currentDatabase)) {
return $this->query('select current_database()')->fetchColumn();
}
}
/**
* Sets the schema search path of the current users session.
* The syntax of the path is a comma separated string with
* your custom search path at the beginning and the "public"
* schema at the end.
*
* This method automatically adds the "public" schema at the
* end of the search path if it is not provied.
* @param string custom schema search path. defaults to public
*/
public function setSchemaSearchPath($searchPath = 'public') {
$schemas = explode(',', str_replace(' ', '', $searchPath));
if (end($schemas) !== 'public') {
$schemas[] = 'public';
}
foreach ($schemas as $k => $item) {
$schemas[$k] = '"' . str_replace(array('"', "'", ';'), '', $item) . '"';
}
$path = implode(', ', $schemas);
$this->exec('SET search_path TO ' . $path);
}
}
...@@ -12,7 +12,7 @@ use yii\db\TableSchema; ...@@ -12,7 +12,7 @@ use yii\db\TableSchema;
use yii\db\ColumnSchema; use yii\db\ColumnSchema;
/** /**
* Schema is the class for retrieving metadata from a PostgreSQL database * Schema is the class for retrieving metadata from a PostgreSQL database
* (version 9.x and above). * (version 9.x and above).
* *
* @author Gevik Babakhani <gevikb@gmail.com> * @author Gevik Babakhani <gevikb@gmail.com>
...@@ -22,62 +22,62 @@ class Schema extends \yii\db\Schema ...@@ -22,62 +22,62 @@ class Schema extends \yii\db\Schema
{ {
/** /**
* The default schema used for the current session. This value is * The default schema used for the current session.
* automatically set to "public" by the PDO driver. * @var string
* @var string
*/ */
public static $DEFAULT_SCHEMA; public $defaultSchema = 'public';
/** /**
* @var array mapping from physical column types (keys) to abstract * @var array mapping from physical column types (keys) to abstract
* column types (values) * column types (values)
*/ */
public $typeMap = array( public $typeMap = array(
'abstime' => self::TYPE_TIMESTAMP, 'abstime' => self::TYPE_TIMESTAMP,
'bit' => self::TYPE_STRING, 'bit' => self::TYPE_STRING,
'boolean' => self::TYPE_BOOLEAN, 'boolean' => self::TYPE_BOOLEAN,
'box' => self::TYPE_STRING, 'box' => self::TYPE_STRING,
'character' => self::TYPE_STRING, 'character' => self::TYPE_STRING,
'bytea' => self::TYPE_BINARY, 'bytea' => self::TYPE_BINARY,
'char' => self::TYPE_STRING, 'char' => self::TYPE_STRING,
'cidr' => self::TYPE_STRING, 'cidr' => self::TYPE_STRING,
'circle' => self::TYPE_STRING, 'circle' => self::TYPE_STRING,
'date' => self::TYPE_DATE, 'date' => self::TYPE_DATE,
'real' => self::TYPE_FLOAT, 'real' => self::TYPE_FLOAT,
'double precision' => self::TYPE_DECIMAL, 'double precision' => self::TYPE_DECIMAL,
'inet' => self::TYPE_STRING, 'inet' => self::TYPE_STRING,
'smallint' => self::TYPE_SMALLINT, 'smallint' => self::TYPE_SMALLINT,
'integer' => self::TYPE_INTEGER, 'integer' => self::TYPE_INTEGER,
'bigint' => self::TYPE_BIGINT, 'bigint' => self::TYPE_BIGINT,
'interval' => self::TYPE_STRING, 'interval' => self::TYPE_STRING,
'json' => self::TYPE_STRING, 'json' => self::TYPE_STRING,
'line' => self::TYPE_STRING, 'line' => self::TYPE_STRING,
'macaddr' => self::TYPE_STRING, 'macaddr' => self::TYPE_STRING,
'money' => self::TYPE_MONEY, 'money' => self::TYPE_MONEY,
'name' => self::TYPE_STRING, 'name' => self::TYPE_STRING,
'numeric' => self::TYPE_STRING, 'numeric' => self::TYPE_STRING,
'numrange' => self::TYPE_DECIMAL, 'numrange' => self::TYPE_DECIMAL,
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal! 'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!
'path' => self::TYPE_STRING, 'path' => self::TYPE_STRING,
'point' => self::TYPE_STRING, 'point' => self::TYPE_STRING,
'polygon' => self::TYPE_STRING, 'polygon' => self::TYPE_STRING,
'text' => self::TYPE_TEXT, 'text' => self::TYPE_TEXT,
'time without time zone' => self::TYPE_TIME, 'time without time zone' => self::TYPE_TIME,
'timestamp without time zone' => self::TYPE_TIMESTAMP, 'timestamp without time zone' => self::TYPE_TIMESTAMP,
'timestamp with time zone' => self::TYPE_TIMESTAMP, 'timestamp with time zone' => self::TYPE_TIMESTAMP,
'time with time zone' => self::TYPE_TIMESTAMP, 'time with time zone' => self::TYPE_TIMESTAMP,
'unknown' => self::TYPE_STRING, 'unknown' => self::TYPE_STRING,
'uuid' => self::TYPE_STRING, 'uuid' => self::TYPE_STRING,
'bit varying' => self::TYPE_STRING, 'bit varying' => self::TYPE_STRING,
'character varying' => self::TYPE_STRING, 'character varying' => self::TYPE_STRING,
'xml' => self::TYPE_STRING 'xml' => self::TYPE_STRING
); );
/** /**
* Creates a query builder for the MySQL database. * Creates a query builder for the MySQL database.
* @return QueryBuilder query builder instance * @return QueryBuilder query builder instance
*/ */
public function createQueryBuilder() { public function createQueryBuilder()
{
return new QueryBuilder($this->db); return new QueryBuilder($this->db);
} }
...@@ -86,7 +86,8 @@ class Schema extends \yii\db\Schema ...@@ -86,7 +86,8 @@ class Schema extends \yii\db\Schema
* @param TableSchema $table the table metadata object * @param TableSchema $table the table metadata object
* @param string $name the table name * @param string $name the table name
*/ */
protected function resolveTableNames($table, $name) { protected function resolveTableNames($table, $name)
{
$parts = explode('.', str_replace('"', '', $name)); $parts = explode('.', str_replace('"', '', $name));
if (isset($parts[1])) { if (isset($parts[1])) {
$table->schemaName = $parts[0]; $table->schemaName = $parts[0];
...@@ -95,7 +96,7 @@ class Schema extends \yii\db\Schema ...@@ -95,7 +96,7 @@ class Schema extends \yii\db\Schema
$table->name = $parts[0]; $table->name = $parts[0];
} }
if ($table->schemaName === null) { if ($table->schemaName === null) {
$table->schemaName = self::$DEFAULT_SCHEMA; $table->schemaName = $this->defaultSchema;
} }
} }
...@@ -105,7 +106,8 @@ class Schema extends \yii\db\Schema ...@@ -105,7 +106,8 @@ class Schema extends \yii\db\Schema
* @param string $name table name * @param string $name table name
* @return string the properly quoted table name * @return string the properly quoted table name
*/ */
public function quoteSimpleTableName($name) { public function quoteSimpleTableName($name)
{
return strpos($name, '"') !== false ? $name : '"' . $name . '"'; return strpos($name, '"') !== false ? $name : '"' . $name . '"';
} }
...@@ -114,12 +116,15 @@ class Schema extends \yii\db\Schema ...@@ -114,12 +116,15 @@ class Schema extends \yii\db\Schema
* @param string $name table name * @param string $name table name
* @return TableSchema|null driver dependent table metadata. Null if the table does not exist. * @return TableSchema|null driver dependent table metadata. Null if the table does not exist.
*/ */
public function loadTableSchema($name) { public function loadTableSchema($name)
{
$table = new TableSchema(); $table = new TableSchema();
$this->resolveTableNames($table, $name); $this->resolveTableNames($table, $name);
if ($this->findColumns($table)) { if ($this->findColumns($table)) {
$this->findConstraints($table); $this->findConstraints($table);
return $table; return $table;
} else {
return null;
} }
} }
...@@ -127,11 +132,11 @@ class Schema extends \yii\db\Schema ...@@ -127,11 +132,11 @@ class Schema extends \yii\db\Schema
* Collects the foreign key column details for the given table. * Collects the foreign key column details for the given table.
* @param TableSchema $table the table metadata * @param TableSchema $table the table metadata
*/ */
protected function findConstraints($table) { protected function findConstraints($table)
{
$tableName = $this->quoteValue($table->name); $tableName = $this->quoteValue($table->name);
$tableSchema = $this->quoteValue($table->schemaName); $tableSchema = $this->quoteValue($table->schemaName);
$database = $this->quoteValue($this->db->pdo->getCurrentDatabase());
//We need to extract the constraints de hard way since: //We need to extract the constraints de hard way since:
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us //http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us
...@@ -158,14 +163,9 @@ where ...@@ -158,14 +163,9 @@ where
ct.contype='f' ct.contype='f'
and c.relname={$tableName} and c.relname={$tableName}
and ns.nspname={$tableSchema} and ns.nspname={$tableSchema}
and current_database() = {$database}
SQL; SQL;
try { $constraints = $this->db->createCommand($sql)->queryAll();
$constraints = $this->db->createCommand($sql)->queryAll();
} catch (\Exception $e) {
return false;
}
foreach ($constraints as $constraint) { foreach ($constraints as $constraint) {
$columns = explode(',', $constraint['columns']); $columns = explode(',', $constraint['columns']);
$fcolumns = explode(',', $constraint['foreign_columns']); $fcolumns = explode(',', $constraint['foreign_columns']);
...@@ -175,7 +175,6 @@ SQL; ...@@ -175,7 +175,6 @@ SQL;
} }
$table->foreignKeys[] = $citem; $table->foreignKeys[] = $citem;
} }
return true;
} }
/** /**
...@@ -183,8 +182,8 @@ SQL; ...@@ -183,8 +182,8 @@ SQL;
* @param TableSchema $table the table metadata * @param TableSchema $table the table metadata
* @return boolean whether the table exists in the database * @return boolean whether the table exists in the database
*/ */
protected function findColumns($table) { protected function findColumns($table)
$dbname = $this->db->quoteValue($this->db->pdo->getCurrentDatabase()); {
$tableName = $this->db->quoteValue($table->name); $tableName = $this->db->quoteValue($table->name);
$schemaName = $this->db->quoteValue($table->schemaName); $schemaName = $this->db->quoteValue($table->schemaName);
$sql = <<<SQL $sql = <<<SQL
...@@ -239,16 +238,15 @@ WHERE ...@@ -239,16 +238,15 @@ WHERE
a.attnum > 0 a.attnum > 0
and c.relname = {$tableName} and c.relname = {$tableName}
and d.nspname = {$schemaName} and d.nspname = {$schemaName}
and current_database() = {$dbname}
ORDER BY ORDER BY
a.attnum; a.attnum;
SQL; SQL;
try { $columns = $this->db->createCommand($sql)->queryAll();
$columns = $this->db->createCommand($sql)->queryAll(); if (empty($columns)) {
} catch (\Exception $e) {
return false; return false;
} }
foreach ($columns as $column) { foreach ($columns as $column) {
$column = $this->loadColumnSchema($column); $column = $this->loadColumnSchema($column);
$table->columns[$column->name] = $column; $table->columns[$column->name] = $column;
...@@ -267,7 +265,8 @@ SQL; ...@@ -267,7 +265,8 @@ SQL;
* @param array $info column information * @param array $info column information
* @return ColumnSchema the column schema object * @return ColumnSchema the column schema object
*/ */
protected function loadColumnSchema($info) { protected function loadColumnSchema($info)
{
$column = new ColumnSchema(); $column = new ColumnSchema();
$column->allowNull = $info['is_nullable']; $column->allowNull = $info['is_nullable'];
$column->autoIncrement = $info['is_autoinc']; $column->autoIncrement = $info['is_autoinc'];
...@@ -290,5 +289,4 @@ SQL; ...@@ -290,5 +289,4 @@ SQL;
$column->phpType = $this->getColumnPhpType($column); $column->phpType = $this->getColumnPhpType($column);
return $column; return $column;
} }
}
}
\ No newline at end of file
...@@ -14,4 +14,4 @@ namespace yii\debug; ...@@ -14,4 +14,4 @@ namespace yii\debug;
class Module extends \yii\base\Module class Module extends \yii\base\Module
{ {
public $controllerNamespace = 'yii\debug\controllers'; public $controllerNamespace = 'yii\debug\controllers';
} }
\ No newline at end of file
...@@ -31,4 +31,4 @@ class DefaultController extends Controller ...@@ -31,4 +31,4 @@ class DefaultController extends Controller
echo "Unable to find debug data tagged with '$tag'."; echo "Unable to find debug data tagged with '$tag'.";
} }
} }
} }
\ No newline at end of file
...@@ -14,5 +14,4 @@ namespace yii\helpers; ...@@ -14,5 +14,4 @@ namespace yii\helpers;
*/ */
class Json extends base\Json class Json extends base\Json
{ {
} }
...@@ -286,7 +286,7 @@ class Console ...@@ -286,7 +286,7 @@ class Console
* You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]]. * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
* @return string * @return string
*/ */
public static function ansiFormat($string, $format=array()) public static function ansiFormat($string, $format = array())
{ {
$code = implode(';', $format); $code = implode(';', $format);
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m";
...@@ -589,11 +589,10 @@ class Console ...@@ -589,11 +589,10 @@ class Console
if (static::isRunningOnWindows()) { if (static::isRunningOnWindows()) {
$output = array(); $output = array();
exec('mode con', $output); exec('mode con', $output);
if(isset($output) && strpos($output[1], 'CON')!==false) { if (isset($output) && strpos($output[1], 'CON') !== false) {
return $size = array((int)preg_replace('~[^0-9]~', '', $output[3]), (int)preg_replace('~[^0-9]~', '', $output[4])); return $size = array((int)preg_replace('~[^0-9]~', '', $output[3]), (int)preg_replace('~[^0-9]~', '', $output[4]));
} }
} else { } else {
// try stty if available // try stty if available
$stty = array(); $stty = array();
if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) { if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) {
......
...@@ -1479,5 +1479,4 @@ class Html ...@@ -1479,5 +1479,4 @@ class Html
$name = strtolower(static::getInputName($model, $attribute)); $name = strtolower(static::getInputName($model, $attribute));
return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name); return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name);
} }
} }
...@@ -50,7 +50,7 @@ class Inflector ...@@ -50,7 +50,7 @@ class Inflector
'/(ax|cris|test)is$/i' => '\1es', '/(ax|cris|test)is$/i' => '\1es',
'/s$/' => 's', '/s$/' => 's',
'/^$/' => '', '/^$/' => '',
'/$/' => 's', '/$/' => 's',
); );
/** /**
* @var array the rules for converting a word into its singular form. * @var array the rules for converting a word into its singular form.
...@@ -94,7 +94,7 @@ class Inflector ...@@ -94,7 +94,7 @@ class Inflector
'/(n)ews$/i' => '\1\2ews', '/(n)ews$/i' => '\1\2ews',
'/eaus$/' => 'eau', '/eaus$/' => 'eau',
'/^(.*us)$/' => '\\1', '/^(.*us)$/' => '\\1',
'/s$/i' => '', '/s$/i' => '',
); );
/** /**
* @var array the special rules for converting a word between its plural form and singular form. * @var array the special rules for converting a word between its plural form and singular form.
...@@ -468,7 +468,7 @@ class Inflector ...@@ -468,7 +468,7 @@ class Inflector
if (in_array(($number % 100), range(11, 13))) { if (in_array(($number % 100), range(11, 13))) {
return $number . 'th'; return $number . 'th';
} }
switch (($number % 10)) { switch ($number % 10) {
case 1: return $number . 'st'; case 1: return $number . 'st';
case 2: return $number . 'nd'; case 2: return $number . 'nd';
case 3: return $number . 'rd'; case 3: return $number . 'rd';
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace yii\helpers\base; namespace yii\helpers\base;
use yii\base\InvalidParamException; use yii\base\InvalidParamException;
use yii\base\Jsonable;
use yii\web\JsExpression; use yii\web\JsExpression;
/** /**
...@@ -90,16 +91,19 @@ class Json ...@@ -90,16 +91,19 @@ class Json
$token = '!{[' . count($expressions) . ']}!'; $token = '!{[' . count($expressions) . ']}!';
$expressions['"' . $token . '"'] = $data->expression; $expressions['"' . $token . '"'] = $data->expression;
return $token; return $token;
} } elseif ($data instanceof Jsonable) {
$result = array(); return $data->toJson();
foreach ($data as $key => $value) { } else {
if (is_array($value) || is_object($value)) { $result = array();
$result[$key] = static::processData($value, $expressions); foreach ($data as $key => $value) {
} else { if (is_array($value) || is_object($value)) {
$result[$key] = $value; $result[$key] = static::processData($value, $expressions);
} else {
$result[$key] = $value;
}
} }
return $result;
} }
return $result;
} else { } else {
return $data; return $data;
} }
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\i18n;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\caching\Cache;
use yii\db\Connection;
use yii\db\Query;
/**
* DbMessageSource extends [[MessageSource]] and represents a message source that stores translated
* messages in database.
*
* The database must contain the following two tables:
*
* ~~~
* CREATE TABLE tbl_source_message (
* id INTEGER PRIMARY KEY,
* category VARCHAR(32),
* message TEXT
* );
*
* CREATE TABLE tbl_message (
* id INTEGER,
* language VARCHAR(16),
* translation TEXT,
* PRIMARY KEY (id, language),
* CONSTRAINT fk_message_source_message FOREIGN KEY (id)
* REFERENCES tbl_source_message (id) ON DELETE CASCADE ON UPDATE RESTRICT
* );
* ~~~
*
* The `tbl_source_message` table stores the messages to be translated, and the `tbl_message` table stores
* the translated messages. The name of these two tables can be customized by setting [[sourceMessageTable]]
* and [[messageTable]], respectively.
*
* @author resurtm <resurtm@gmail.com>
* @since 2.0
*/
class DbMessageSource extends MessageSource
{
/**
* Prefix which would be used when generating cache key.
*/
const CACHE_KEY_PREFIX = 'DbMessageSource';
/**
* @var Connection|string the DB connection object or the application component ID of the DB connection.
* After the DbMessageSource object is created, if you want to change this property, you should only assign
* it with a DB connection object.
*/
public $db = 'db';
/**
* @var Cache|string the cache object or the application component ID of the cache object.
* The messages data will be cached using this cache object. Note, this property has meaning only
* in case [[cachingDuration]] set to non-zero value.
* After the DbMessageSource object is created, if you want to change this property, you should only assign
* it with a cache object.
*/
public $cache = 'cache';
/**
* @var string the name of the source message table.
*/
public $sourceMessageTable = 'tbl_source_message';
/**
* @var string the name of the translated message table.
*/
public $messageTable = 'tbl_message';
/**
* @var integer the time in seconds that the messages can remain valid in cache.
* Use 0 to indicate that the cached data will never expire.
* @see enableCaching
*/
public $cachingDuration = 0;
/**
* @var boolean whether to enable caching translated messages
*/
public $enableCaching = false;
/**
* Initializes the DbMessageSource component.
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
* Configured [[cache]] component would also be initialized.
* @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid.
*/
public function init()
{
parent::init();
if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db);
}
if (!$this->db instanceof Connection) {
throw new InvalidConfigException("DbMessageSource::db must be either a DB connection instance or the application component ID of a DB connection.");
}
if ($this->enableCaching) {
if (is_string($this->cache)) {
$this->cache = Yii::$app->getComponent($this->cache);
}
if (!$this->cache instanceof Cache) {
throw new InvalidConfigException("DbMessageSource::cache must be either a cache object or the application component ID of the cache object.");
}
}
}
/**
* Loads the message translation for the specified language and category.
* Child classes should override this method to return the message translations of
* the specified language and category.
* @param string $category the message category
* @param string $language the target language
* @return array the loaded messages. The keys are original messages, and the values
* are translated messages.
*/
protected function loadMessages($category, $language)
{
if ($this->enableCaching) {
$key = array(
__CLASS__,
$category,
$language,
);
$messages = $this->cache->get($key);
if ($messages === false) {
$messages = $this->loadMessagesFromDb($category, $language);
$this->cache->set($key, $messages, $this->cachingDuration);
}
return $messages;
} else {
return $this->loadMessagesFromDb($category, $language);
}
}
/**
* Loads the messages from database.
* You may override this method to customize the message storage in the database.
* @param string $category the message category.
* @param string $language the target language.
* @return array the messages loaded from database.
*/
protected function loadMessagesFromDb($category, $language)
{
$query = new Query();
$messages = $query->select(array('t1.message message', 't2.translation translation'))
->from(array($this->sourceMessageTable . ' t1', $this->messageTable . ' t2'))
->where('t1.id = t2.id AND t1.category = :category AND t2.language = :language')
->params(array(':category' => $category, ':language' => $language))
->createCommand($this->db)
->queryAll();
return ArrayHelper::map($messages, 'message', 'translation');
}
}
...@@ -78,16 +78,11 @@ class I18N extends Component ...@@ -78,16 +78,11 @@ class I18N extends Component
* @param string $category the message category. * @param string $category the message category.
* @param string $message the message to be translated. * @param string $message the message to be translated.
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message. * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
* @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current * @param string $language the language code (e.g. `en_US`, `en`).
* [[\yii\base\Application::language|application language]] will be used.
* @return string the translated message. * @return string the translated message.
*/ */
public function translate($category, $message, $params = array(), $language = null) public function translate($category, $message, $params, $language)
{ {
if ($language === null) {
$language = Yii::$app->language;
}
$message = $this->getMessageSource($category)->translate($category, $message, $language); $message = $this->getMessageSource($category)->translate($category, $message, $language);
if (!is_array($params)) { if (!is_array($params)) {
......
...@@ -118,4 +118,3 @@ class MessageSource extends Component ...@@ -118,4 +118,3 @@ class MessageSource extends Component
} }
} }
} }
...@@ -125,7 +125,7 @@ class Accordion extends Widget ...@@ -125,7 +125,7 @@ class Accordion extends Widget
$items[] = Html::tag($headerTag, $item['header'], $headerOptions); $items[] = Html::tag($headerTag, $item['header'], $headerOptions);
$options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array())); $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array()));
$tag = ArrayHelper::remove($options, 'tag', 'div'); $tag = ArrayHelper::remove($options, 'tag', 'div');
$items[] = Html::tag($tag, $item['content'], $options);; $items[] = Html::tag($tag, $item['content'], $options);
} }
return implode("\n", $items); return implode("\n", $items);
......
...@@ -10,7 +10,6 @@ namespace yii\jui; ...@@ -10,7 +10,6 @@ namespace yii\jui;
use Yii; use Yii;
use yii\helpers\Json; use yii\helpers\Json;
/** /**
* Menu renders a menu jQuery UI widget. * Menu renders a menu jQuery UI widget.
* *
......
...@@ -10,7 +10,6 @@ namespace yii\jui; ...@@ -10,7 +10,6 @@ namespace yii\jui;
use Yii; use Yii;
use yii\helpers\Json; use yii\helpers\Json;
/** /**
* \yii\jui\Widget is the base class for all jQuery UI widgets. * \yii\jui\Widget is the base class for all jQuery UI widgets.
* *
......
...@@ -493,8 +493,9 @@ class DbManager extends Manager ...@@ -493,8 +493,9 @@ class DbManager extends Manager
'bizRule' => $row['biz_rule'], 'bizRule' => $row['biz_rule'],
'data' => $data, 'data' => $data,
)); ));
} else } else {
return null; return null;
}
} }
/** /**
......
...@@ -468,7 +468,7 @@ class PhpManager extends Manager ...@@ -468,7 +468,7 @@ class PhpManager extends Manager
'bizRule' => $assignment['bizRule'], 'bizRule' => $assignment['bizRule'],
'data' => $assignment['data'], 'data' => $assignment['data'],
)); ));
} }
} }
} }
} }
......
...@@ -45,4 +45,4 @@ return array( ...@@ -45,4 +45,4 @@ return array(
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support', 'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use <abbr title="Internationalized domain names">IDN</abbr>-feature of EmailValidator or UrlValidator or the <code>yii\i18n\Formatter</code> class.' 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use <abbr title="Internationalized domain names">IDN</abbr>-feature of EmailValidator or UrlValidator or the <code>yii\i18n\Formatter</code> class.'
), ),
); );
\ No newline at end of file
...@@ -117,4 +117,3 @@ class CaptchaValidator extends Validator ...@@ -117,4 +117,3 @@ class CaptchaValidator extends Validator
return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');'; return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');';
} }
} }
...@@ -73,4 +73,3 @@ class DateValidator extends Validator ...@@ -73,4 +73,3 @@ class DateValidator extends Validator
return DateTime::createFromFormat($this->format, $value) !== false; return DateTime::createFromFormat($this->format, $value) !== false;
} }
} }
...@@ -40,4 +40,3 @@ class DefaultValueValidator extends Validator ...@@ -40,4 +40,3 @@ class DefaultValueValidator extends Validator
} }
} }
} }
...@@ -99,4 +99,3 @@ class ExistValidator extends Validator ...@@ -99,4 +99,3 @@ class ExistValidator extends Validator
return $query->exists(); return $query->exists();
} }
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
namespace yii\validators; namespace yii\validators;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
/** /**
......
...@@ -35,7 +35,7 @@ class RangeValidator extends Validator ...@@ -35,7 +35,7 @@ class RangeValidator extends Validator
* @var boolean whether to invert the validation logic. Defaults to false. If set to true, * @var boolean whether to invert the validation logic. Defaults to false. If set to true,
* the attribute value should NOT be among the list of values defined via [[range]]. * the attribute value should NOT be among the list of values defined via [[range]].
**/ **/
public $not = false; public $not = false;
/** /**
* Initializes the validator. * Initializes the validator.
......
...@@ -32,7 +32,7 @@ class RegularExpressionValidator extends Validator ...@@ -32,7 +32,7 @@ class RegularExpressionValidator extends Validator
* the regular expression defined via [[pattern]] should NOT match the attribute value. * the regular expression defined via [[pattern]] should NOT match the attribute value.
* @throws InvalidConfigException if the "pattern" is not a valid regular expression * @throws InvalidConfigException if the "pattern" is not a valid regular expression
**/ **/
public $not = false; public $not = false;
/** /**
* Initializes the validator. * Initializes the validator.
......
...@@ -174,4 +174,3 @@ class StringValidator extends Validator ...@@ -174,4 +174,3 @@ class StringValidator extends Validator
return 'yii.validation.string(value, messages, ' . json_encode($options) . ');'; return 'yii.validation.string(value, messages, ' . json_encode($options) . ');';
} }
} }
...@@ -99,7 +99,7 @@ class UrlValidator extends Validator ...@@ -99,7 +99,7 @@ class UrlValidator extends Validator
} }
if ($this->enableIDN) { if ($this->enableIDN) {
$value = preg_replace_callback('/:\/\/([^\/]+)/', function($matches) { $value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) {
return '://' . idn_to_ascii($matches[1]); return '://' . idn_to_ascii($matches[1]);
}, $value); }, $value);
} }
......
...@@ -179,7 +179,7 @@ abstract class Validator extends Component ...@@ -179,7 +179,7 @@ abstract class Validator extends Component
} }
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
$skip = $this->skipOnError && $object->hasErrors($attribute) $skip = $this->skipOnError && $object->hasErrors($attribute)
|| $this->skipOnEmpty && $this->isEmpty($object->$attribute); || $this->skipOnEmpty && $this->isEmpty($object->$attribute);
if (!$skip) { if (!$skip) {
$this->validateAttribute($object, $attribute); $this->validateAttribute($object, $attribute);
} }
......
...@@ -11,7 +11,6 @@ use Yii; ...@@ -11,7 +11,6 @@ use Yii;
use yii\base\Object; use yii\base\Object;
use ArrayIterator; use ArrayIterator;
/** /**
* HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers.
* *
......
<?php <?php
/** /**
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\web; namespace yii\web;
use Yii; use Yii;
use yii\base\ActionFilter; use yii\base\ActionFilter;
use yii\base\Action; use yii\base\Action;
use yii\base\View; use yii\base\View;
use yii\caching\Dependency; use yii\caching\Dependency;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class PageCache extends ActionFilter class PageCache extends ActionFilter
{ {
/** /**
* @var boolean whether the content being cached should be differentiated according to the route. * @var boolean whether the content being cached should be differentiated according to the route.
* A route consists of the requested controller ID and action ID. Defaults to true. * A route consists of the requested controller ID and action ID. Defaults to true.
*/ */
public $varyByRoute = true; public $varyByRoute = true;
/** /**
* @var string the application component ID of the [[\yii\caching\Cache|cache]] object. * @var string the application component ID of the [[\yii\caching\Cache|cache]] object.
*/ */
public $cache = 'cache'; public $cache = 'cache';
/** /**
* @var integer number of seconds that the data can remain valid in cache. * @var integer number of seconds that the data can remain valid in cache.
* Use 0 to indicate that the cached data will never expire. * Use 0 to indicate that the cached data will never expire.
*/ */
public $duration = 60; public $duration = 60;
/** /**
* @var array|Dependency the dependency that the cached content depends on. * @var array|Dependency the dependency that the cached content depends on.
* This can be either a [[Dependency]] object or a configuration array for creating the dependency object. * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.
* For example, * For example,
* *
* ~~~ * ~~~
* array( * array(
* 'class' => 'yii\caching\DbDependency', * 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(lastModified) FROM Post', * 'sql' => 'SELECT MAX(lastModified) FROM Post',
* ) * )
* ~~~ * ~~~
* *
* would make the output cache depends on the last modified time of all posts. * would make the output cache depends on the last modified time of all posts.
* If any post has its modification time changed, the cached content would be invalidated. * If any post has its modification time changed, the cached content would be invalidated.
*/ */
public $dependency; public $dependency;
/** /**
* @var array list of factors that would cause the variation of the content being cached. * @var array list of factors that would cause the variation of the content being cached.
* Each factor is a string representing a variation (e.g. the language, a GET parameter). * Each factor is a string representing a variation (e.g. the language, a GET parameter).
* The following variation setting will cause the content to be cached in different versions * The following variation setting will cause the content to be cached in different versions
* according to the current application language: * according to the current application language:
* *
* ~~~ * ~~~
* array( * array(
* Yii::$app->language, * Yii::$app->language,
* ) * )
*/ */
public $variations; public $variations;
/** /**
* @var boolean whether to enable the fragment cache. You may use this property to turn on and off * @var boolean whether to enable the fragment cache. You may use this property to turn on and off
* the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
*/ */
public $enabled = true; public $enabled = true;
public function init() public function init()
{ {
parent::init(); parent::init();
if ($this->view === null) { if ($this->view === null) {
$this->view = Yii::$app->getView(); $this->view = Yii::$app->getView();
} }
} }
/** /**
* This method is invoked right before an action is to be executed (after all possible filters.) * This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action. * You may override this method to do last-minute preparation for the action.
* @param Action $action the action to be executed. * @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed. * @return boolean whether the action should continue to be executed.
*/ */
public function beforeAction($action) public function beforeAction($action)
{ {
$properties = array(); $properties = array();
foreach (array('cache', 'duration', 'dependency', 'variations', 'enabled') as $name) { foreach (array('cache', 'duration', 'dependency', 'variations', 'enabled') as $name) {
$properties[$name] = $this->$name; $properties[$name] = $this->$name;
} }
$id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__; $id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__;
return $this->view->beginCache($id, $properties); return $this->view->beginCache($id, $properties);
} }
/** /**
* This method is invoked right after an action is executed. * This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action. * You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed. * @param Action $action the action just executed.
*/ */
public function afterAction($action) public function afterAction($action)
{ {
$this->view->endCache(); $this->view->endCache();
} }
} }
...@@ -792,4 +792,3 @@ class Request extends \yii\base\Request ...@@ -792,4 +792,3 @@ class Request extends \yii\base\Request
} }
} }
} }
...@@ -9,8 +9,10 @@ namespace yii\web; ...@@ -9,8 +9,10 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\HttpException; use yii\base\HttpException;
use yii\base\InvalidParamException;
use yii\helpers\FileHelper; use yii\helpers\FileHelper;
use yii\helpers\Html; use yii\helpers\Html;
use yii\helpers\Json;
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
/** /**
...@@ -27,9 +29,122 @@ class Response extends \yii\base\Response ...@@ -27,9 +29,122 @@ class Response extends \yii\base\Response
* @see redirect * @see redirect
*/ */
public $ajaxRedirectCode = 278; public $ajaxRedirectCode = 278;
/**
* @var string
*/
public $content;
/**
* @var string
*/
public $statusText;
/**
* @var string the charset to use. If not set, [[\yii\base\Application::charset]] will be used.
*/
public $charset;
/**
* @var string the version of the HTTP protocol to use
*/
public $version = '1.0';
/**
* @var array list of HTTP status codes and the corresponding texts
*/
public static $statusTexts = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
118 => 'Connection timed out',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
210 => 'Content Different',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Reserved',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
310 => 'Too many Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Time-out',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested range unsatisfiable',
417 => 'Expectation failed',
418 => 'I’m a teapot',
422 => 'Unprocessable entity',
423 => 'Locked',
424 => 'Method failure',
425 => 'Unordered Collection',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
449 => 'Retry With',
450 => 'Blocked by Windows Parental Controls',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway ou Proxy Error',
503 => 'Service Unavailable',
504 => 'Gateway Time-out',
505 => 'HTTP Version not supported',
507 => 'Insufficient storage',
508 => 'Loop Detected',
509 => 'Bandwidth Limit Exceeded',
510 => 'Not Extended',
511 => 'Network Authentication Required',
);
private $_statusCode = 200;
/**
* @var HeaderCollection
*/
private $_headers; private $_headers;
public function init()
{
if ($this->charset === null) {
$this->charset = Yii::$app->charset;
}
}
public function getStatusCode()
{
return $this->_statusCode;
}
public function setStatusCode($value)
{
$this->_statusCode = (int)$value;
if ($this->isInvalid()) {
throw new InvalidParamException("The HTTP status code is invalid: $value");
}
$this->statusText = isset(self::$statusTexts[$this->_statusCode]) ? self::$statusTexts[$this->_statusCode] : '';
}
/** /**
* Returns the header collection. * Returns the header collection.
* The header collection contains the currently registered HTTP headers. * The header collection contains the currently registered HTTP headers.
...@@ -43,6 +158,52 @@ class Response extends \yii\base\Response ...@@ -43,6 +158,52 @@ class Response extends \yii\base\Response
return $this->_headers; return $this->_headers;
} }
public function renderJson($data)
{
$this->getHeaders()->set('content-type', 'application/json');
$this->content = Json::encode($data);
}
public function renderJsonp($data, $callbackName)
{
$this->getHeaders()->set('content-type', 'text/javascript');
$data = Json::encode($data);
$this->content = "$callbackName($data);";
}
/**
* Sends the response to the client.
* @return boolean true if the response was sent
*/
public function send()
{
$this->sendHeaders();
$this->sendContent();
}
/**
* Sends the response headers to the client
*/
protected function sendHeaders()
{
header("HTTP/{$this->version} " . $this->getStatusCode() . " {$this->statusText}");
foreach ($this->_headers as $name => $values) {
foreach ($values as $value) {
header("$name: $value");
}
}
$this->_headers->removeAll();
}
/**
* Sends the response content to the client
*/
protected function sendContent()
{
echo $this->content;
$this->content = null;
}
/** /**
* Sends a file to user. * Sends a file to user.
* @param string $fileName file name * @param string $fileName file name
...@@ -201,7 +362,7 @@ class Response extends \yii\base\Response ...@@ -201,7 +362,7 @@ class Response extends \yii\base\Response
} }
if (!isset($options['mimeType'])) { if (!isset($options['mimeType'])) {
if (($options['mimeType'] = CFileHelper::getMimeTypeByExtension($filePath)) === null) { if (($options['mimeType'] = FileHelper::getMimeTypeByExtension($filePath)) === null) {
$options['mimeType'] = 'text/plain'; $options['mimeType'] = 'text/plain';
} }
} }
...@@ -303,4 +464,84 @@ class Response extends \yii\base\Response ...@@ -303,4 +464,84 @@ class Response extends \yii\base\Response
{ {
return Yii::$app->getRequest()->getCookies(); return Yii::$app->getRequest()->getCookies();
} }
/**
* @return boolean whether this response has a valid [[statusCode]].
*/
public function isInvalid()
{
return $this->getStatusCode() < 100 || $this->getStatusCode() >= 600;
}
/**
* @return boolean whether this response is informational
*/
public function isInformational()
{
return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200;
}
/**
* @return boolean whether this response is successfully
*/
public function isSuccessful()
{
return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300;
}
/**
* @return boolean whether this response is a redirection
*/
public function isRedirection()
{
return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400;
}
/**
* @return boolean whether this response indicates a client error
*/
public function isClientError()
{
return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500;
}
/**
* @return boolean whether this response indicates a server error
*/
public function isServerError()
{
return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600;
}
/**
* @return boolean whether this response is OK
*/
public function isOk()
{
return 200 === $this->getStatusCode();
}
/**
* @return boolean whether this response indicates the current request is forbidden
*/
public function isForbidden()
{
return 403 === $this->getStatusCode();
}
/**
* @return boolean whether this response indicates the currently requested resource is not found
*/
public function isNotFound()
{
return 404 === $this->getStatusCode();
}
/**
* @return boolean whether this response is empty
*/
public function isEmpty()
{
return in_array($this->getStatusCode(), array(201, 204, 304));
}
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace yii\web; namespace yii\web;
use yii\widgets\Html; use yii\helpers\Html;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
......
...@@ -221,7 +221,7 @@ class User extends Component ...@@ -221,7 +221,7 @@ class User extends Component
if ($destroySession) { if ($destroySession) {
Yii::$app->getSession()->destroy(); Yii::$app->getSession()->destroy();
} }
$this->afterLogout($identity); $this->afterLogout($identity);
} }
} }
......
<?php <?php
/** /**
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\widgets; namespace yii\widgets;
use Yii; use Yii;
use yii\base\Widget; use yii\base\Widget;
use yii\caching\Cache; use yii\caching\Cache;
use yii\caching\Dependency; use yii\caching\Dependency;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class FragmentCache extends Widget class FragmentCache extends Widget
{ {
/** /**
* @var Cache|string the cache object or the application component ID of the cache object. * @var Cache|string the cache object or the application component ID of the cache object.
* After the FragmentCache object is created, if you want to change this property, * After the FragmentCache object is created, if you want to change this property,
* you should only assign it with a cache object. * you should only assign it with a cache object.
*/ */
public $cache = 'cache'; public $cache = 'cache';
/** /**
* @var integer number of seconds that the data can remain valid in cache. * @var integer number of seconds that the data can remain valid in cache.
* Use 0 to indicate that the cached data will never expire. * Use 0 to indicate that the cached data will never expire.
*/ */
public $duration = 60; public $duration = 60;
/** /**
* @var array|Dependency the dependency that the cached content depends on. * @var array|Dependency the dependency that the cached content depends on.
* This can be either a [[Dependency]] object or a configuration array for creating the dependency object. * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.
* For example, * For example,
* *
* ~~~ * ~~~
* array( * array(
* 'class' => 'yii\caching\DbDependency', * 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(lastModified) FROM Post', * 'sql' => 'SELECT MAX(lastModified) FROM Post',
* ) * )
* ~~~ * ~~~
* *
* would make the output cache depends on the last modified time of all posts. * would make the output cache depends on the last modified time of all posts.
* If any post has its modification time changed, the cached content would be invalidated. * If any post has its modification time changed, the cached content would be invalidated.
*/ */
public $dependency; public $dependency;
/** /**
* @var array list of factors that would cause the variation of the content being cached. * @var array list of factors that would cause the variation of the content being cached.
* Each factor is a string representing a variation (e.g. the language, a GET parameter). * Each factor is a string representing a variation (e.g. the language, a GET parameter).
* The following variation setting will cause the content to be cached in different versions * The following variation setting will cause the content to be cached in different versions
* according to the current application language: * according to the current application language:
* *
* ~~~ * ~~~
* array( * array(
* Yii::$app->language, * Yii::$app->language,
* ) * )
*/ */
public $variations; public $variations;
/** /**
* @var boolean whether to enable the fragment cache. You may use this property to turn on and off * @var boolean whether to enable the fragment cache. You may use this property to turn on and off
* the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
*/ */
public $enabled = true; public $enabled = true;
/** /**
* @var array a list of placeholders for embedding dynamic contents. This property * @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. * is used internally to implement the content caching feature. Do not modify it.
*/ */
public $dynamicPlaceholders; public $dynamicPlaceholders;
/** /**
* Initializes the FragmentCache object. * Initializes the FragmentCache object.
*/ */
public function init() public function init()
{ {
parent::init(); parent::init();
if (!$this->enabled) { if (!$this->enabled) {
$this->cache = null; $this->cache = null;
} elseif (is_string($this->cache)) { } elseif (is_string($this->cache)) {
$this->cache = Yii::$app->getComponent($this->cache); $this->cache = Yii::$app->getComponent($this->cache);
} }
if ($this->getCachedContent() === false) { if ($this->getCachedContent() === false) {
$this->view->cacheStack[] = $this; $this->view->cacheStack[] = $this;
ob_start(); ob_start();
ob_implicit_flush(false); ob_implicit_flush(false);
} }
} }
/** /**
* Marks the end of content to be cached. * Marks the end of content to be cached.
* Content displayed before this method call and after {@link init()} * Content displayed before this method call and after {@link init()}
* will be captured and saved in cache. * will be captured and saved in cache.
* This method does nothing if valid content is already found in cache. * This method does nothing if valid content is already found in cache.
*/ */
public function run() public function run()
{ {
if (($content = $this->getCachedContent()) !== false) { if (($content = $this->getCachedContent()) !== false) {
echo $content; echo $content;
} elseif ($this->cache instanceof Cache) { } elseif ($this->cache instanceof Cache) {
$content = ob_get_clean(); $content = ob_get_clean();
array_pop($this->view->cacheStack); array_pop($this->view->cacheStack);
if (is_array($this->dependency)) { if (is_array($this->dependency)) {
$this->dependency = Yii::createObject($this->dependency); $this->dependency = Yii::createObject($this->dependency);
} }
$data = array($content, $this->dynamicPlaceholders); $data = array($content, $this->dynamicPlaceholders);
$this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency); $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
if (empty($this->view->cacheStack) && !empty($this->dynamicPlaceholders)) { if (empty($this->view->cacheStack) && !empty($this->dynamicPlaceholders)) {
$content = $this->updateDynamicContent($content, $this->dynamicPlaceholders); $content = $this->updateDynamicContent($content, $this->dynamicPlaceholders);
} }
echo $content; echo $content;
} }
} }
/** /**
* @var string|boolean the cached content. False if the content is not cached. * @var string|boolean the cached content. False if the content is not cached.
*/ */
private $_content; private $_content;
/** /**
* Returns the cached content if available. * Returns the cached content if available.
* @return string|boolean the cached content. False is returned if valid content is not found in the cache. * @return string|boolean the cached content. False is returned if valid content is not found in the cache.
*/ */
public function getCachedContent() public function getCachedContent()
{ {
if ($this->_content === null) { if ($this->_content === null) {
$this->_content = false; $this->_content = false;
if ($this->cache instanceof Cache) { if ($this->cache instanceof Cache) {
$key = $this->calculateKey(); $key = $this->calculateKey();
$data = $this->cache->get($key); $data = $this->cache->get($key);
if (is_array($data) && count($data) === 2) { if (is_array($data) && count($data) === 2) {
list ($content, $placeholders) = $data; list ($content, $placeholders) = $data;
if (is_array($placeholders) && count($placeholders) > 0) { if (is_array($placeholders) && count($placeholders) > 0) {
if (empty($this->view->cacheStack)) { if (empty($this->view->cacheStack)) {
// outermost cache: replace placeholder with dynamic content // outermost cache: replace placeholder with dynamic content
$content = $this->updateDynamicContent($content, $placeholders); $content = $this->updateDynamicContent($content, $placeholders);
} }
foreach ($placeholders as $name => $statements) { foreach ($placeholders as $name => $statements) {
$this->view->addDynamicPlaceholder($name, $statements); $this->view->addDynamicPlaceholder($name, $statements);
} }
} }
$this->_content = $content; $this->_content = $content;
} }
} }
} }
return $this->_content; return $this->_content;
} }
protected function updateDynamicContent($content, $placeholders) protected function updateDynamicContent($content, $placeholders)
{ {
foreach ($placeholders as $name => $statements) { foreach ($placeholders as $name => $statements) {
$placeholders[$name] = $this->view->evaluateDynamicContent($statements); $placeholders[$name] = $this->view->evaluateDynamicContent($statements);
} }
return strtr($content, $placeholders); return strtr($content, $placeholders);
} }
/** /**
* Generates a unique key used for storing the content in cache. * Generates a unique key used for storing the content in cache.
* The key generated depends on both [[id]] and [[variations]]. * The key generated depends on both [[id]] and [[variations]].
* @return mixed a valid cache key * @return mixed a valid cache key
*/ */
protected function calculateKey() protected function calculateKey()
{ {
$factors = array(__CLASS__, $this->getId()); $factors = array(__CLASS__, $this->getId());
if (is_array($this->variations)) { if (is_array($this->variations)) {
foreach ($this->variations as $factor) { foreach ($this->variations as $factor) {
$factors[] = $factor; $factors[] = $factor;
} }
} }
return $factors; return $factors;
} }
} }
...@@ -91,5 +91,4 @@ class ListPager extends Widget ...@@ -91,5 +91,4 @@ class ListPager extends Widget
'{page}' => $page + 1, '{page}' => $page + 1,
)); ));
} }
} }
...@@ -22,9 +22,6 @@ return array( ...@@ -22,9 +22,6 @@ return array(
'dsn' => 'pgsql:host=localhost;dbname=yiitest;port=5432;', 'dsn' => 'pgsql:host=localhost;dbname=yiitest;port=5432;',
'username' => 'postgres', 'username' => 'postgres',
'password' => 'postgres', 'password' => 'postgres',
'attributes' => array(
'search_path' => 'master,hello'
),
'fixture' => __DIR__ . '/postgres.sql', 'fixture' => __DIR__ . '/postgres.sql',
) )
) )
......
...@@ -32,6 +32,7 @@ class ResponseTest extends \yiiunit\TestCase ...@@ -32,6 +32,7 @@ class ResponseTest extends \yiiunit\TestCase
protected function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
$this->mockApplication();
$this->reset(); $this->reset();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment