Commit c5816d23 by Carsten Brandt

Merge branch 'master' of git.yiisoft.com:yii2

* 'master' of git.yiisoft.com:yii2: Added Command::batchInsert() exception cleanup. error handling cleanup. console app cleanup. MVC cleanup MVC WIP MVC WIP MVC WIP cleanup. MVC WIP refactored logging. MVC WIP MVC WIP patched controller and action creation todo updates. Conflicts: framework/console/View.php
parents f1b62a47 a3963851
......@@ -8,9 +8,8 @@
*/
use yii\base\Exception;
use yii\logging\Logger;
use yii\base\InvalidCallException;
use yii\base\InvalidConfigException;
use yii\logging\Logger;
/**
* Gets the application start timestamp.
......@@ -30,6 +29,11 @@ defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);
* This constant defines the framework installation directory.
*/
defined('YII_PATH') or define('YII_PATH', __DIR__);
/**
* This constant defines whether error handling should be enabled. Defaults to true.
*/
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
/**
* YiiBase is the core helper class for the Yii framework.
......@@ -121,8 +125,8 @@ class YiiBase
*
* To import a class or a directory, one can use either path alias or class name (can be namespaced):
*
* - `@app/components/GoogleMap`: importing the `GoogleMap` class with a path alias;
* - `@app/components/*`: importing the whole `components` directory with a path alias;
* - `@application/components/GoogleMap`: importing the `GoogleMap` class with a path alias;
* - `@application/components/*`: importing the whole `components` directory with a path alias;
* - `GoogleMap`: importing the `GoogleMap` class with a class name. [[autoload()]] will be used
* when this class is used for the first time.
*
......@@ -189,14 +193,14 @@ class YiiBase
*
* Note, this method does not ensure the existence of the resulting path.
* @param string $alias alias
* @param boolean $throwException whether to throw exception if the alias is invalid.
* @return string|boolean path corresponding to the alias, false if the root alias is not previously registered.
* @throws Exception if the alias is invalid and $throwException is true.
* @see setAlias
*/
public static function getAlias($alias, $throwException = false)
public static function getAlias($alias)
{
if (isset(self::$aliases[$alias])) {
if (!is_string($alias)) {
return false;
} elseif (isset(self::$aliases[$alias])) {
return self::$aliases[$alias];
} elseif ($alias === '' || $alias[0] !== '@') { // not an alias
return $alias;
......@@ -206,11 +210,7 @@ class YiiBase
return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
}
}
if ($throwException) {
throw new Exception("Invalid path alias: $alias");
} else {
return false;
}
return false;
}
/**
......@@ -322,12 +322,12 @@ class YiiBase
* the class. For example,
*
* - `\app\components\GoogleMap`: fully-qualified namespaced class.
* - `@app/components/GoogleMap`: an alias
* - `@application/components/GoogleMap`: an alias
*
* Below are some usage examples:
*
* ~~~
* $object = \Yii::createObject('@app/components/GoogleMap');
* $object = \Yii::createObject('@application/components/GoogleMap');
* $object = \Yii::createObject(array(
* 'class' => '\app\components\GoogleMap',
* 'apiKey' => 'xyz',
......@@ -361,7 +361,7 @@ class YiiBase
$class = $config['class'];
unset($config['class']);
} else {
throw new InvalidCallException('Object configuration must be an array containing a "class" element.');
throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
}
if (!class_exists($class, false)) {
......
......@@ -9,8 +9,6 @@
namespace yii\base;
use yii\util\ReflectionHelper;
/**
* Action is the base class for all controller action classes.
*
......@@ -21,6 +19,14 @@ use yii\util\ReflectionHelper;
* will be invoked by the controller when the action is requested.
* The `run()` method can have parameters which will be filled up
* with user input values automatically according to their names.
* For example, if the `run()` method is declared as follows:
*
* ~~~
* public function run($id, $type = 'book') { ... }
* ~~~
*
* And the parameters provided for the action are: `array('id' => 1)`.
* Then the `run()` method will be invoked as `run(1)` automatically.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -37,6 +43,7 @@ class Action extends Component
public $controller;
/**
* Constructor.
* @param string $id the ID of this action
* @param Controller $controller the controller that owns this action
* @param array $config name-value pairs that will be used to initialize the object properties
......@@ -51,20 +58,45 @@ class Action extends Component
/**
* Runs this action with the specified parameters.
* This method is mainly invoked by the controller.
* @param array $params action parameters
* @param array $params the parameters to be bound to the action's run() method.
* @return integer the exit status (0 means normal, non-zero means abnormal).
* @throws InvalidConfigException if the action class does not have a run() method
*/
public function runWithParams($params)
{
try {
$ps = ReflectionHelper::extractMethodParams($this, 'run', $params);
} catch (Exception $e) {
$this->controller->invalidActionParams($this, $e);
return 1;
if (!method_exists($this, 'run')) {
throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
}
if ($params !== $ps) {
$this->controller->extraActionParams($this, $ps, $params);
$method = new \ReflectionMethod($this, 'run');
$args = $this->bindActionParams($method, $params);
return (int)$method->invokeArgs($this, $args);
}
/**
* Binds the given parameters to the action method.
* The returned array contains the parameters that need to be passed to the action method.
* This method calls [[Controller::validateActionParams()]] to check if any exception
* should be raised if there are missing or unknown parameters.
* @param \ReflectionMethod $method the action method reflection object
* @param array $params the supplied parameters
* @return array the parameters that can be passed to the action method
*/
protected function bindActionParams($method, $params)
{
$args = array();
$missing = array();
foreach ($method->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
$args[] = $params[$name];
unset($params[$name]);
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
$missing[] = $name;
}
}
return (int)call_user_func_array(array($this, 'run'), $ps);
$this->controller->validateActionParams($this, $missing, $params);
return $args;
}
}
......@@ -12,8 +12,7 @@ namespace yii\base;
/**
* ActionEvent represents the event parameter used for an action event.
*
* By setting the [[isValid]] property, one may control whether to continue the life cycle of
* the action currently being executed.
* By setting the [[isValid]] property, one may control whether to continue running the action.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -25,7 +24,7 @@ class ActionEvent extends Event
*/
public $action;
/**
* @var boolean whether the action is in valid state and its life cycle should proceed.
* @var boolean whether to continue running the action.
*/
public $isValid = true;
......@@ -34,7 +33,7 @@ class ActionEvent extends Event
* @param Action $action the action associated with this action event.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct(Action $action, $config = array())
public function __construct($action, $config = array())
{
$this->action = $action;
parent::__construct($config);
......
<?php
/**
* ActionFilter class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* ActionFilter is the base class for all action filters.
*
* A filter can be applied to a controller action at different stages of its life cycle. In particular,
* it responds to the following events that are raised when an action is being executed:
*
* 1. authorize
* 2. beforeAction
* 3. beforeRender
* 4. afterRender
* 5. afterAction
*
* Derived classes may respond to these events by overriding the corresponding methods in this class.
* For example, to create an access control filter, one may override the [[authorize()]] method.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActionFilter extends Behavior
{
/**
* @var Controller the owner of this behavior. For action filters, this should be a controller object.
*/
public $owner;
/**
* @var array IDs of actions that this filter applies to.
* If this property is empty or not set, it means this filter applies to all actions.
* Note that if an action appears in [[except]], the filter will not apply to this action, even
* if the action also appears in [[only]].
* @see exception
*/
public $only;
/**
* @var array IDs of actions that this filter does NOT apply to.
*/
public $except;
public function init()
{
$this->owner->on('authorize', array($this, 'handleEvent'));
$this->owner->on('beforeAction', array($this, 'handleEvent'));
$this->owner->on('beforeRender', array($this, 'handleEvent'));
$this->owner->getEventHandlers('afterRender')->insertAt(0, array($this, 'handleEvent'));
$this->owner->getEventHandlers('afterAction')->insertAt(0, array($this, 'handleEvent'));
}
public function authorize($event)
{
}
public function beforeAction($event)
{
}
public function beforeRender($event)
{
}
public function afterRender($event)
{
}
public function afterAction($event)
{
}
public function handleEvent($event)
{
if ($this->applyTo($event->action)) {
$this->{$event->name}($event);
}
}
public function applyTo(Action $action)
{
return (empty($this->only) || in_array($action->id, $this->only, false) !== false)
&& (empty($this->except) || in_array($action->id, $this->except, false) === false);
}
}
\ No newline at end of file
......@@ -52,90 +52,46 @@ class ErrorHandler extends Component
* @var \Exception the exception that is being handled currently
*/
public $exception;
/**
* @var boolean whether to log errors also using error_log(). Defaults to true.
* Note that errors captured by the error handler are always logged by [[\Yii::error()]].
*/
public $logErrors = true;
public function init()
{
set_exception_handler(array($this, 'handleException'));
set_error_handler(array($this, 'handleError'), error_reporting());
}
/**
* Handles PHP execution errors such as warnings, notices.
*
* This method is used as a PHP error handler. It will simply raise an `ErrorException`.
*
* @param integer $code the level of the error raised
* @param string $message the error message
* @param string $file the filename that the error was raised in
* @param integer $line the line number the error was raised at
* @throws \ErrorException the error exception
*/
public function handleError($code, $message, $file, $line)
{
if(error_reporting()!==0) {
throw new \ErrorException($message, 0, $code, $file, $line);
}
}
/**
* @param \Exception $exception
*/
public function handleException($exception)
public function handle($exception)
{
// disable error capturing to avoid recursive errors while handling exceptions
restore_error_handler();
restore_exception_handler();
$this->exception = $exception;
$this->logException($exception);
if ($this->discardExistingOutput) {
$this->clearOutput();
}
try {
$this->render($exception);
} catch (\Exception $e) {
// use the most primitive way to display exception thrown in the error view
$this->renderAsText($e);
}
try {
\Yii::$application->end(1);
} catch (Exception $e2) {
// use the most primitive way to log error occurred in end()
$msg = get_class($e2) . ': ' . $e2->getMessage() . ' (' . $e2->getFile() . ':' . $e2->getLine() . ")\n";
$msg .= $e2->getTraceAsString() . "\n";
$msg .= "Previous error:\n";
$msg .= $e2->getTraceAsString() . "\n";
$msg .= '$_SERVER=' . var_export($_SERVER, true);
error_log($msg);
exit(1);
}
$this->render($exception);
}
protected function render($exception)
{
if ($this->errorAction !== null) {
\Yii::$application->runController($this->errorAction);
\Yii::$application->runAction($this->errorAction);
} elseif (\Yii::$application instanceof \yii\web\Application) {
if (!headers_sent()) {
$errorCode = $exception instanceof HttpException ? $exception->statusCode : 500;
header("HTTP/1.0 $errorCode " . get_class($exception));
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
$this->renderAsText($exception);
\Yii::$application->renderException($exception);
} else {
$this->renderAsHtml($exception);
$view = new View($this);
if (!YII_DEBUG || $exception instanceof Exception && $exception->causedByUser) {
$viewName = $this->errorView;
} else {
$viewName = $this->exceptionView;
}
echo $view->render($viewName, array(
'exception' => $exception,
));
}
} else {
$this->renderAsText($exception);
\Yii::$application->renderException($exception);
}
}
......@@ -286,22 +242,6 @@ class ErrorHandler extends Component
return htmlspecialchars($text, ENT_QUOTES, \Yii::$application->charset);
}
public function logException($exception)
{
$category = get_class($exception);
if ($exception instanceof HttpException) {
/** @var $exception HttpException */
$category .= '\\' . $exception->statusCode;
} elseif ($exception instanceof \ErrorException) {
/** @var $exception \ErrorException */
$category .= '\\' . $exception->getSeverity();
}
\Yii::error((string)$exception, $category);
if ($this->logErrors) {
error_log($exception);
}
}
public function clearOutput()
{
// the following manual level counting is to deal with zlib.output_compression set to On
......@@ -313,22 +253,14 @@ class ErrorHandler extends Component
/**
* @param \Exception $exception
*/
public function renderAsText($exception)
public function renderAsHtml($exception)
{
if (YII_DEBUG) {
echo $exception;
$view = new View($this);
if (!YII_DEBUG || $exception instanceof Exception && $exception->causedByUser) {
$viewName = $this->errorView;
} else {
echo get_class($exception) . ': ' . $exception->getMessage();
$viewName = $this->exceptionView;
}
}
/**
* @param \Exception $exception
*/
public function renderAsHtml($exception)
{
$view = new View;
$view->context = $this;
$name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView;
echo $view->render($name, array(
'exception' => $exception,
......
......@@ -17,5 +17,17 @@ namespace yii\base;
*/
class Exception extends \Exception
{
/**
* @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
*/
public $causedByUser = false;
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Exception');
}
}
......@@ -25,6 +25,10 @@ class HttpException extends Exception
* @var integer HTTP status code, such as 403, 404, 500, etc.
*/
public $statusCode;
/**
* @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
*/
public $causedByUser = true;
/**
* Constructor.
......
......@@ -9,13 +9,11 @@
namespace yii\base;
use yii\util\ReflectionHelper;
/**
* InlineAction represents an action that is defined as a controller method.
*
* The name of the controller method should be in the format of `actionXyz`
* where `Xyz` stands for the action ID (e.g. `actionIndex`).
* The name of the controller method is available via [[actionMethod]] which
* is set by the [[controller]] who creates this action.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -23,6 +21,23 @@ use yii\util\ReflectionHelper;
class InlineAction extends Action
{
/**
* @var string the controller method that this inline action is associated with
*/
public $actionMethod;
/**
* @param string $id the ID of this action
* @param Controller $controller the controller that owns this action
* @param string $actionMethod the controller method that this inline action is associated with
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($id, $controller, $actionMethod, $config = array())
{
$this->actionMethod = $actionMethod;
parent::__construct($id, $controller, $config);
}
/**
* Runs this action with the specified parameters.
* This method is mainly invoked by the controller.
* @param array $params action parameters
......@@ -30,16 +45,8 @@ class InlineAction extends Action
*/
public function runWithParams($params)
{
try {
$method = 'action' . $this->id;
$ps = ReflectionHelper::extractMethodParams($this->controller, $method, $params);
} catch (Exception $e) {
$this->controller->invalidActionParams($this, $e);
return 1;
}
if ($params !== $ps) {
$this->controller->extraActionParams($this, $ps, $params);
}
return (int)call_user_func_array(array($this->controller, $method), $ps);
$method = new \ReflectionMethod($this->controller, $this->actionMethod);
$args = $this->bindActionParams($method, $params);
return (int)$method->invokeArgs($this->controller, $args);
}
}
......@@ -17,5 +17,12 @@ namespace yii\base;
*/
class InvalidCallException extends \Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Invalid Call');
}
}
......@@ -17,5 +17,12 @@ namespace yii\base;
*/
class InvalidConfigException extends \Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Invalid Configuration');
}
}
......@@ -17,5 +17,17 @@ namespace yii\base;
*/
class InvalidRequestException extends \Exception
{
/**
* @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
*/
public $causedByUser = true;
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Invalid Request');
}
}
<?php
/**
* InvalidRouteException class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* InvalidRouteException represents an exception caused by an invalid route.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InvalidRouteException extends \Exception
{
/**
* @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
*/
public $causedByUser = true;
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Invalid Route');
}
}
......@@ -17,5 +17,12 @@ namespace yii\base;
*/
class NotSupportedException extends \Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Not Supported');
}
}
......@@ -9,55 +9,114 @@
namespace yii\base;
use Yii;
use yii\base\InvalidConfigException;
use yii\util\FileHelper;
/**
* Theme represents an application theme.
*
* A theme is directory consisting of view and layout files which are meant to replace their
* non-themed counterparts.
*
* Theme uses [[pathMap]] to achieve the file replacement. A view or layout file will be replaced
* with its themed version if part of its path matches one of the keys in [[pathMap]].
* Then the matched part will be replaced with the corresponding array value.
*
* For example, if [[pathMap]] is `array('/www/views' => '/www/themes/basic')`,
* then the themed version for a view file `/www/views/site/index.php` will be
* `/www/themes/basic/site/index.php`.
*
* @property string $baseUrl the base URL for this theme. This is mainly used by [[getUrl()]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Theme extends Component
{
/**
* @var string the root path of this theme.
* @see pathMap
*/
public $basePath;
public $baseUrl;
/**
* @var array the mapping between view directories and their corresponding themed versions.
* If not set, it will be initialized as a mapping from [[Application::basePath]] to [[basePath]].
* This property is used by [[apply()]] when a view is trying to apply the theme.
*/
public $pathMap;
private $_baseUrl;
/**
* Initializes the theme.
* @throws InvalidConfigException if [[basePath]] is not set.
*/
public function init()
{
if ($this->basePath !== null) {
$this->basePath = \Yii::getAlias($this->basePath, true);
} else {
throw new InvalidConfigException("Theme.basePath must be set.");
parent::init();
if (empty($this->pathMap)) {
if ($this->basePath !== null) {
$this->basePath = FileHelper::ensureDirectory($this->basePath);
$this->pathMap = array(Yii::$application->getBasePath() => $this->basePath);
} else {
throw new InvalidConfigException("Theme::basePath must be set.");
}
}
if ($this->baseUrl !== null) {
$this->baseUrl = \Yii::getAlias($this->baseUrl, true);
} else {
throw new InvalidConfigException("Theme.baseUrl must be set.");
$paths = array();
foreach ($this->pathMap as $from => $to) {
$paths[FileHelper::normalizePath($from) . DIRECTORY_SEPARATOR] = FileHelper::normalizePath($to) . DIRECTORY_SEPARATOR;
}
$this->pathMap = $paths;
}
/**
* Returns the base URL for this theme.
* The method [[getUrl()]] will prefix this to the given URL.
* @return string the base URL for this theme.
*/
public function getBaseUrl()
{
return $this->_baseUrl;
}
/**
* Sets the base URL for this theme.
* @param string $value the base URL for this theme.
*/
public function setBaseUrl($value)
{
$this->_baseUrl = rtrim(Yii::getAlias($value), '/');
}
/**
* @param Application|Module|Controller|Object $context
* @return string
* Converts a file to a themed file if possible.
* If there is no corresponding themed file, the original file will be returned.
* @param string $path the file to be themed
* @return string the themed file, or the original file if the themed version is not available.
*/
public function getViewPath($context = null)
public function apply($path)
{
$viewPath = $this->basePath . DIRECTORY_SEPARATOR . 'views';
if ($context === null || $context instanceof Application) {
return $viewPath;
} elseif ($context instanceof Controller || $context instanceof Module) {
return $viewPath . DIRECTORY_SEPARATOR . $context->getUniqueId();
} else {
return $viewPath . DIRECTORY_SEPARATOR . str_replace('\\', '_', get_class($context));
$path = FileHelper::normalizePath($path);
foreach ($this->pathMap as $from => $to) {
if (strpos($path, $from) === 0) {
$n = strlen($from);
$file = $to . substr($path, $n);
if (is_file($file)) {
return $file;
}
}
}
return $path;
}
/**
* @param Module $module
* @return string
* Converts a relative URL into an absolute URL using [[basePath]].
* @param string $url the relative URL to be converted.
* @return string the absolute URL
*/
public function getLayoutPath($module = null)
public function getUrl($url)
{
return $this->getViewPath($module) . DIRECTORY_SEPARATOR . 'layouts';
return $this->baseUrl . '/' . ltrim($url, '/');
}
}
......@@ -17,5 +17,12 @@ namespace yii\base;
*/
class UnknownMethodException extends \Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Unknown Method');
}
}
......@@ -17,5 +17,12 @@ namespace yii\base;
*/
class UnknownPropertyException extends \Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Unknown Property');
}
}
......@@ -49,7 +49,7 @@ class Widget extends Component
public function getId($autoGenerate = true)
{
if ($autoGenerate && $this->_id === null) {
$this->_id = 'yw' . self::$_counter++;
$this->_id = 'w' . self::$_counter++;
}
return $this->_id;
}
......@@ -80,7 +80,7 @@ class Widget extends Component
* To determine which view file should be rendered, the method calls [[findViewFile()]] which
* will search in the directories as specified by [[basePath]].
*
* View name can be a path alias representing an absolute file path (e.g. `@app/views/layout/index`),
* View name can be a path alias representing an absolute file path (e.g. `@application/views/layout/index`),
* or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given
* in the view name.
*
......@@ -102,4 +102,16 @@ class Widget extends Component
{
return new View($this);
}
/**
* Returns the directory containing the view files for this widget.
* The default implementation returns the 'views' subdirectory under the directory containing the widget class file.
* @return string the directory containing the view files for this widget.
*/
public function getViewPath()
{
$className = get_class($this);
$class = new \ReflectionClass($className);
return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
}
}
\ No newline at end of file
......@@ -10,7 +10,7 @@
namespace yii\console;
use yii\base\Exception;
use yii\util\ReflectionHelper;
use yii\base\InvalidRouteException;
/**
* Application represents a console application.
......@@ -85,40 +85,36 @@ class Application extends \yii\base\Application
* Processes the request.
* The request is represented in terms of a controller route and action parameters.
* @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
* @throws Exception if the route cannot be resolved into a controller
*/
public function processRequest()
{
/** @var $request Request */
$request = $this->getRequest();
if ($request->getIsConsoleRequest()) {
return $this->runController($request->route, $request->params);
return $this->runAction($request->route, $request->params);
} else {
die('This script must be run from the command line.');
echo "Error: this script must be run from the command line.";
return 1;
}
}
/**
* Runs a controller with the given route and parameters.
* @param string $route the route (e.g. `post/create`)
* @param array $params the parameters to be passed to the controller action
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
* @throws Exception if the route cannot be resolved into a controller
* Runs a controller action specified by a route.
* 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.
* If the route is empty, the method will use [[defaultRoute]].
* @param string $route the route that specifies 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.
*/
public function runController($route, $params = array())
public function runAction($route, $params = array())
{
$result = $this->createController($route);
if ($result === false) {
throw new Exception(\Yii::t('yii', 'Unable to resolve the request.'));
try {
return parent::runAction($route, $params);
} catch (InvalidRouteException $e) {
echo "Error: unknown command \"$route\".\n";
return 1;
}
/** @var $controller \yii\console\Controller */
list($controller, $action) = $result;
$priorController = $this->controller;
$this->controller = $controller;
$params = ReflectionHelper::initObjectWithParams($controller, $params);
$status = $controller->run($action, $params);
$this->controller = $priorController;
return $status;
}
/**
......@@ -152,4 +148,9 @@ class Application extends \yii\base\Application
),
));
}
public function usageError($message)
{
}
}
......@@ -9,8 +9,10 @@
namespace yii\console;
use Yii;
use yii\base\Action;
use yii\base\Exception;
use yii\base\InvalidRequestException;
use yii\base\InvalidRouteException;
/**
* Controller is the base class of console command classes.
......@@ -30,72 +32,56 @@ use yii\base\Exception;
class Controller extends \yii\base\Controller
{
/**
* This method is invoked when the request parameters do not satisfy the requirement of the specified action.
* The default implementation will throw an exception.
* @param Action $action the action being executed
* @param Exception $exception the exception about the invalid parameters
* @var boolean whether the call of [[confirm()]] requires a user input.
* If false, [[confirm()]] will always return true no matter what user enters or not.
*/
public function invalidActionParams($action, $exception)
{
echo \Yii::t('yii', 'Error: {message}', array(
'{message}' => $exception->getMessage(),
));
\Yii::$application->end(1);
}
public $interactive = true;
/**
* This method is invoked when extra parameters are provided to an action while it is executed.
* @param Action $action the action being executed
* @param array $expected the expected action parameters (name => value)
* @param array $actual the actual action parameters (name => value)
* Runs an action with the specified action ID and parameters.
* If the action ID is empty, the method will use [[defaultAction]].
* @param string $id the ID of the action to be executed.
* @param array $params the parameters (name-value pairs) to be passed to the action.
* @return integer the status of the action execution. 0 means normal, other values mean abnormal.
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction
*/
public function extraActionParams($action, $expected, $actual)
public function runAction($id, $params = array())
{
unset($expected['args'], $actual['args']);
$keys = array_diff(array_keys($actual), array_keys($expected));
if (!empty($keys)) {
echo \Yii::t('yii', 'Error: Unknown parameter(s): {params}', array(
'{params}' => implode(', ', $keys),
)) . "\n";
\Yii::$application->end(1);
if ($params !== array()) {
$class = new \ReflectionClass($this);
foreach ($params as $name => $value) {
if ($class->hasProperty($name)) {
$property = $class->getProperty($name);
if ($property->isPublic() && !$property->isStatic() && $property->getDeclaringClass()->getName() === get_class($this)) {
$this->$name = $value;
unset($params[$name]);
}
}
}
}
return parent::runAction($id, $params);
}
/**
* Reads input via the readline PHP extension if that's available, or fgets() if readline is not installed.
*
* @param string $message to echo out before waiting for user input
* @param string $default the default string to be returned when user does not write anything.
* Defaults to null, means that default string is disabled.
* @return mixed line read as a string, or false if input has been closed
* Validates the parameter being bound to actions.
* This method is invoked when parameters are being bound to the currently requested action.
* Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
* @param Action $action the currently requested action
* @param array $missingParams the names of the missing parameters
* @param array $unknownParams the unknown parameters (name=>value)
* @throws InvalidRequestException if there are missing or unknown parameters
*/
public function prompt($message, $default = null)
public function validateActionParams($action, $missingParams, $unknownParams)
{
if($default !== null) {
$message .= " [$default] ";
}
else {
$message .= ' ';
}
if(extension_loaded('readline')) {
$input = readline($message);
if($input !== false) {
readline_add_history($input);
}
}
else {
echo $message;
$input = fgets(STDIN);
}
if($input === false) {
return false;
}
else {
$input = trim($input);
return ($input === '' && $default !== null) ? $default : $input;
if (!empty($missingParams)) {
throw new InvalidRequestException(Yii::t('yii', 'Missing required options: {params}', array(
'{params}' => implode(', ', $missingParams),
)));
} elseif (!empty($unknownParams)) {
throw new InvalidRequestException(Yii::t('yii', 'Unknown options: {params}', array(
'{params}' => implode(', ', $unknownParams),
)));
}
}
......@@ -108,9 +94,23 @@ class Controller extends \yii\base\Controller
*/
public function confirm($message, $default = false)
{
echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
if ($this->interactive) {
echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
$input = trim(fgets(STDIN));
return empty($input) ? $default : !strncasecmp($input, 'y', 1);
} else {
return true;
}
}
public function usageError($message)
{
echo "\nError: $message\n";
Yii::$application->end(1);
}
$input = trim(fgets(STDIN));
return empty($input) ? $default : !strncasecmp($input, 'y', 1);
public function globalOptions()
{
return array();
}
}
\ No newline at end of file
......@@ -165,8 +165,8 @@ class CreateController extends Controller
}
/**
* @param string $path1 abosolute path
* @param string $path2 abosolute path
* @param string $path1 absolute path
* @param string $path2 absolute path
*
* @return string relative path
*/
......
......@@ -12,6 +12,7 @@ namespace yii\console\controllers;
use yii\base\Application;
use yii\base\InlineAction;
use yii\console\Controller;
use yii\util\StringHelper;
/**
* This command provides help information about console commands.
......@@ -54,16 +55,16 @@ class HelpController extends Controller
} else {
$result = \Yii::$application->createController($args[0]);
if ($result === false) {
echo "Unknown command: " . $args[0] . "\n";
echo "Error: no help for unknown command \"{$args[0]}\".\n";
return 1;
}
list($controller, $action) = $result;
list($controller, $actionID) = $result;
if ($action === '') {
if ($actionID === '') {
$status = $this->getControllerHelp($controller);
} else {
$status = $this->getActionHelp($controller, $action);
$status = $this->getActionHelp($controller, $actionID);
}
}
return $status;
......@@ -87,13 +88,13 @@ class HelpController extends Controller
*/
public function getActions($controller)
{
$actions = array_keys($controller->actionMap);
$actions = array_keys($controller->actions());
$class = new \ReflectionClass($controller);
foreach ($class->getMethods() as $method) {
/** @var $method \ReflectionMethod */
$name = $method->getName();
if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0) {
$actions[] = lcfirst(substr($name, 6));
if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
$actions[] = StringHelper::camel2id(substr($name, 6));
}
}
sort($actions);
......@@ -107,11 +108,7 @@ class HelpController extends Controller
*/
protected function getModuleCommands($module)
{
if ($module instanceof Application) {
$prefix = '';
} else {
$prefix = $module->getUniqueId() . '/';
}
$prefix = $module instanceof Application ? '' : $module->getUniqueID() . '/';
$commands = array();
foreach (array_keys($module->controllerMap) as $id) {
......@@ -145,12 +142,12 @@ class HelpController extends Controller
{
$commands = $this->getCommands();
if ($commands !== array()) {
echo "\n Usage: yiic <command-name> [...options...]\n\n";
echo "The following commands are available:\n";
echo "\nUsage: yiic <command-name> [...options...]\n\n";
echo "The following commands are available:\n\n";
foreach ($commands as $command) {
echo " - $command\n";
echo " * $command\n";
}
echo "\nTo see individual command help, enter:\n";
echo "\nTo see the help of each command, enter:\n";
echo "\n yiic help <command-name>\n";
} else {
echo "\nNo commands are found.\n";
......@@ -195,7 +192,7 @@ class HelpController extends Controller
$prefix = $controller->getUniqueId();
foreach ($actions as $action) {
if ($controller->defaultAction === $action) {
echo " * $prefix/$action (default)\n";
echo " * $prefix (default)\n";
} else {
echo " * $prefix/$action\n";
}
......@@ -216,7 +213,7 @@ class HelpController extends Controller
{
$action = $controller->createAction($actionID);
if ($action === null) {
echo "Unknown sub-command: " . $controller->getUniqueId() . "/$actionID\n";
echo 'Error: no help for unknown sub-command "' . $controller->getUniqueId() . "/$actionID\".\n";
return 1;
}
if ($action instanceof InlineAction) {
......@@ -312,7 +309,7 @@ class HelpController extends Controller
{
$options = array();
foreach ($class->getProperties() as $property) {
if (!$property->isPublic() || $property->isStatic() || $property->getDeclaringClass()->getName() === 'yii\base\Controller') {
if (!$property->isPublic() || $property->isStatic() || $property->getDeclaringClass()->getName() !== get_class($controller)) {
continue;
}
$name = $property->getName();
......
<?php
/**
* ShellController class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\console\controllers;
use yii\console\Controller;
/**
* This command executes the specified Web application and provides a shell for interaction.
*
* @property string $help The help information for the shell command.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ShellController extends Controller
{
/**
* @return string the help information for the shell command
*/
public function getHelp()
{
return <<<EOD
USAGE
yiic shell [entry-script | config-file]
DESCRIPTION
This command allows you to interact with a Web application
on the command line. It also provides tools to automatically
generate new controllers, views and data models.
It is recommended that you execute this command under
the directory that contains the entry script file of
the Web application.
PARAMETERS
* entry-script | config-file: optional, the path to
the entry script file or the configuration file for
the Web application. If not given, it is assumed to be
the 'index.php' file under the current directory.
EOD;
}
/**
* Execute the action.
* @param array $args command line parameters specific for this command
*/
public function run($args)
{
if(!isset($args[0]))
$args[0]='index.php';
$entryScript=isset($args[0]) ? $args[0] : 'index.php';
if(($entryScript=realpath($args[0]))===false || !is_file($entryScript))
$this->usageError("{$args[0]} does not exist or is not an entry script file.");
// fake the web server setting
$cwd=getcwd();
chdir(dirname($entryScript));
$_SERVER['SCRIPT_NAME']='/'.basename($entryScript);
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
$_SERVER['SCRIPT_FILENAME']=$entryScript;
$_SERVER['HTTP_HOST']='localhost';
$_SERVER['SERVER_NAME']='localhost';
$_SERVER['SERVER_PORT']=80;
// reset context to run the web application
restore_error_handler();
restore_exception_handler();
Yii::setApplication(null);
Yii::setPathOfAlias('application',null);
ob_start();
$config=require($entryScript);
ob_end_clean();
// oops, the entry script turns out to be a config file
if(is_array($config))
{
chdir($cwd);
$_SERVER['SCRIPT_NAME']='/index.php';
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
$_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php';
Yii::createWebApplication($config);
}
restore_error_handler();
restore_exception_handler();
$yiiVersion=Yii::getVersion();
echo <<<EOD
Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion})
Please type 'help' for help. Type 'exit' to quit.
EOD;
$this->runShell();
}
protected function runShell()
{
// disable E_NOTICE so that the shell is more friendly
error_reporting(E_ALL ^ E_NOTICE);
$_runner_=new CConsoleCommandRunner;
$_runner_->addCommands(dirname(__FILE__).'/shell');
$_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell'));
if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false)
$_runner_->addCommands($_path_);
$_commands_=$_runner_->commands;
$log=\Yii::$application->log;
while(($_line_=$this->prompt("\n>>"))!==false)
{
$_line_=trim($_line_);
if($_line_==='exit')
return;
try
{
$_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY);
if(isset($_args_[0]) && isset($_commands_[$_args_[0]]))
{
$_command_=$_runner_->createCommand($_args_[0]);
array_shift($_args_);
$_command_->init();
$_command_->run($_args_);
}
else
echo eval($_line_.';');
}
catch(Exception $e)
{
if($e instanceof ShellException)
echo $e->getMessage();
else
echo $e;
}
}
}
}
class ShellException extends CException
{
}
\ No newline at end of file
......@@ -485,16 +485,41 @@ class Command extends \yii\base\Component
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column data (name=>value) to be inserted into the table.
* @param array $params the parameters to be bound to the command
* @return Command the command object itself
*/
public function insert($table, $columns, $params = array())
public function insert($table, $columns)
{
$params = array();
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
return $this->setSql($sql)->bindValues($params);
}
/**
* Creates a batch INSERT command.
* For example,
*
* ~~~
* $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
* array('Tom', 30),
* array('Jane', 20),
* array('Linda', 25),
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return Command the command object itself
*/
public function batchInsert($table, $columns, $rows)
{
$sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows);
return $this->setSql($sql);
}
/**
* Creates an UPDATE command.
* For example,
*
......
......@@ -34,4 +34,12 @@ class Exception extends \yii\base\Exception
$this->errorInfo = $errorInfo;
parent::__construct($message, $code);
}
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Database Exception');
}
}
\ No newline at end of file
......@@ -115,6 +115,33 @@ class QueryBuilder extends \yii\base\Object
}
/**
* Generates a batch INSERT SQL statement.
* For example,
*
* ~~~
* $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
* array('Tom', 30),
* array('Jane', 20),
* array('Linda', 25),
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @param array $params the parameters to be bound to the command
* @return string the batch INSERT SQL statement
* @throws NotSupportedException if this is not supported by the underlying DBMS
*/
public function batchInsert($table, $columns, $rows, $params = array())
{
throw new NotSupportedException($this->db->getDriverName() . ' does not support batch insert.');
}
/**
* Creates an UPDATE SQL statement.
* For example,
*
......
......@@ -129,4 +129,39 @@ class QueryBuilder extends \yii\db\QueryBuilder
{
return 'SET FOREIGN_KEY_CHECKS=' . ($check ? 1 : 0);
}
/**
* Generates a batch INSERT SQL statement.
* For example,
*
* ~~~
* $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
* array('Tom', 30),
* array('Jane', 20),
* array('Linda', 25),
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return string the batch INSERT SQL statement
*/
public function batchInsert($table, $columns, $rows)
{
$values = array();
foreach ($rows as $row) {
$vs = array();
foreach ($row as $value) {
$vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
}
$values[] = $vs;
}
return 'INSERT INTO ' . $this->db->quoteTableName($table)
. ' (' . implode(', ', $columns) . ') VALUES ('
. implode(', ', $values) . ')';
}
}
......@@ -89,16 +89,17 @@ class DbTarget extends Target
}
/**
* Stores log [[messages]] to DB.
* @param boolean $final whether this method is called at the end of the current application
* Stores log messages to DB.
* @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
* of each message.
*/
public function exportMessages($final)
public function export($messages)
{
$db = $this->getDb();
$tableName = $db->quoteTableName($this->tableName);
$sql = "INSERT INTO $tableName (level, category, log_time, message) VALUES (:level, :category, :log_time, :message)";
$command = $db->createCommand($sql);
foreach ($this->messages as $message) {
foreach ($messages as $message) {
$command->bindValues(array(
':level' => $message[1],
':category' => $message[2],
......
......@@ -39,13 +39,14 @@ class EmailTarget extends Target
public $headers = array();
/**
* Sends log [[messages]] to specified email addresses.
* @param boolean $final whether this method is called at the end of the current application
* Sends log messages to specified email addresses.
* @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
* of each message.
*/
public function exportMessages($final)
public function export($messages)
{
$body = '';
foreach ($this->messages as $message) {
foreach ($messages as $message) {
$body .= $this->formatMessage($message);
}
$body = wordwrap($body, 70);
......
......@@ -65,19 +65,28 @@ class FileTarget extends Target
}
/**
* Sends log [[messages]] to specified email addresses.
* @param boolean $final whether this method is called at the end of the current application
* Sends log messages to specified email addresses.
* @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
* of each message.
*/
public function exportMessages($final)
public function export($messages)
{
$text = '';
foreach ($messages as $message) {
$text .= $this->formatMessage($message);
}
$fp = @fopen($this->logFile, 'a');
@flock($fp, LOCK_EX);
if (@filesize($this->logFile) > $this->maxFileSize * 1024) {
$this->rotateFiles();
@flock($fp,LOCK_UN);
@fclose($fp);
@file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
} else {
@fwrite($fp, $text);
@flock($fp,LOCK_UN);
@fclose($fp);
}
$messages = array();
foreach ($this->messages as $message) {
$messages[] = $this->formatMessage($message);
}
@file_put_contents($this->logFile, implode('', $messages), FILE_APPEND | LOCK_EX);
}
/**
......
......@@ -8,16 +8,13 @@
*/
namespace yii\logging;
use yii\base\Event;
use yii\base\Exception;
use yii\base\InvalidConfigException;
/**
* Logger records logged messages in memory.
*
* When [[flushInterval()]] is reached or when application terminates, it will
* call [[flush()]] to send logged messages to different log targets, such as
* file, email, Web.
* When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]]
* to send logged messages to different log targets, such as file, email, Web.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -25,15 +22,6 @@ use yii\base\Exception;
class Logger extends \yii\base\Component
{
/**
* @event Event an event that is triggered when [[flush()]] is called.
*/
const EVENT_FLUSH = 'flush';
/**
* @event Event an event that is triggered when [[flush()]] is called at the end of application.
*/
const EVENT_FINAL_FLUSH = 'finalFlush';
/**
* Error message level. An error message is one that indicates the abnormal termination of the
* application and may require developer's handling.
*/
......@@ -82,7 +70,7 @@ class Logger extends \yii\base\Component
*
* ~~~
* array(
* [0] => message (mixed)
* [0] => message (mixed, can be a string or some complex data, such as an exception object)
* [1] => level (integer)
* [2] => category (string)
* [3] => timestamp (float, obtained by microtime(true))
......@@ -90,6 +78,10 @@ class Logger extends \yii\base\Component
* ~~~
*/
public $messages = array();
/**
* @var Router the log target router registered with this logger.
*/
public $router;
/**
* Initializes the logger by registering [[flush()]] as a shutdown function.
......@@ -138,7 +130,9 @@ class Logger extends \yii\base\Component
*/
public function flush($final = false)
{
$this->trigger($final ? self::EVENT_FINAL_FLUSH : self::EVENT_FLUSH);
if ($this->router) {
$this->router->dispatch($this->messages, $final);
}
$this->messages = array();
}
......@@ -149,7 +143,7 @@ class Logger extends \yii\base\Component
* of [[YiiBase]] class file.
* @return float the total elapsed time in seconds for current request.
*/
public function getExecutionTime()
public function getElapsedTime()
{
return microtime(true) - YII_BEGIN_TIME;
}
......@@ -218,7 +212,7 @@ class Logger extends \yii\base\Component
if (($last = array_pop($stack)) !== null && $last[0] === $token) {
$timings[] = array($token, $category, $timestamp - $last[3]);
} else {
throw new Exception("Unmatched profiling block: $token");
throw new InvalidConfigException("Unmatched profiling block: $token");
}
}
}
......@@ -231,5 +225,4 @@ class Logger extends \yii\base\Component
return $timings;
}
}
......@@ -81,26 +81,21 @@ class Router extends Component
$this->targets[$name] = Yii::createObject($target);
}
}
Yii::getLogger()->on(Logger::EVENT_FLUSH, array($this, 'processMessages'));
Yii::getLogger()->on(Logger::EVENT_FINAL_FLUSH, array($this, 'processMessages'));
Yii::getLogger()->router = $this;
}
/**
* Retrieves and processes log messages from the system logger.
* This method mainly serves the event handler to the [[Logger::EVENT_FLUSH]] event
* and the [[Logger::EVENT_FINAL_FLUSH]] event.
* It will retrieve the available log messages from the [[Yii::getLogger()|system logger]]
* and invoke the registered [[targets|log targets]] to do the actual processing.
* @param \yii\base\Event $event event parameter
* Dispatches log messages to [[targets]].
* This method is called by [[Logger]] when its [[Logger::flush()]] method is called.
* It will forward the messages to each log target registered in [[targets]].
* @param array $messages the messages to be processed
* @param boolean $final whether this is the final call during a request cycle
*/
public function processMessages($event)
public function dispatch($messages, $final = false)
{
$messages = Yii::getLogger()->messages;
$final = $event->name === Logger::EVENT_FINAL_FLUSH;
foreach ($this->targets as $target) {
if ($target->enabled) {
$target->processMessages($messages, $final);
$target->collect($messages, $final);
}
}
}
......
......@@ -50,15 +50,6 @@ abstract class Target extends \yii\base\Component
*/
public $except = array();
/**
* @var boolean whether to prefix each log message with the current session ID. Defaults to false.
*/
public $prefixSession = false;
/**
* @var boolean whether to prefix each log message with the current user name and ID. Defaults to false.
* @see \yii\web\User
*/
public $prefixUser = false;
/**
* @var boolean whether to log a message containing the current user name and ID. Defaults to false.
* @see \yii\web\User
*/
......@@ -77,19 +68,18 @@ abstract class Target extends \yii\base\Component
public $exportInterval = 1000;
/**
* @var array the messages that are retrieved from the logger so far by this log target.
* @see autoExport
*/
public $messages = array();
private $_messages = array();
private $_levels = 0;
/**
* Exports log messages to a specific destination.
* Child classes must implement this method. Note that you may need
* to clean up [[messages]] in this method to avoid re-exporting messages.
* @param boolean $final whether this method is called at the end of the current application
* Child classes must implement this method.
* @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
* of each message.
*/
abstract public function exportMessages($final);
abstract public function export($messages);
/**
* Processes the given log messages.
......@@ -99,45 +89,16 @@ abstract class Target extends \yii\base\Component
* of each message.
* @param boolean $final whether this method is called at the end of the current application
*/
public function processMessages($messages, $final)
public function collect($messages, $final)
{
$messages = $this->filterMessages($messages);
$this->messages = array_merge($this->messages, $messages);
$count = count($this->messages);
$this->_messages = array($this->_messages, $this->filterMessages($messages));
$count = count($this->_messages);
if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
$this->prepareExport($final);
$this->exportMessages($final);
$this->messages = array();
}
}
/**
* Prepares the [[messages]] for exporting.
* This method will modify each message by prepending extra information
* if [[prefixSession]] and/or [[prefixUser]] are set true.
* It will also add an additional message showing context information if
* [[logUser]] and/or [[logVars]] are set.
* @param boolean $final whether this method is called at the end of the current application
*/
protected function prepareExport($final)
{
$prefix = array();
if ($this->prefixSession && ($id = session_id()) !== '') {
$prefix[] = "[$id]";
}
if ($this->prefixUser && ($user = \Yii::$application->getComponent('user', false)) !== null) {
$prefix[] = '[' . $user->getName() . ']';
$prefix[] = '[' . $user->getId() . ']';
}
if ($prefix !== array()) {
$prefix = implode(' ', $prefix);
foreach ($this->messages as $i => $message) {
$this->messages[$i][0] = $prefix . ' ' . $this->messages[$i][0];
if (($context = $this->getContextMessage()) !== '') {
$this->_messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
}
}
if ($final && ($context = $this->getContextMessage()) !== '') {
$this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
$this->export($this->_messages);
$this->_messages = array();
}
}
......@@ -164,7 +125,7 @@ abstract class Target extends \yii\base\Component
/**
* @return integer the message levels that this target is interested in. This is a bitmap of
* level values. Defaults to 0, meaning all available levels.
* level values. Defaults to 0, meaning all available levels.
*/
public function getLevels()
{
......
......@@ -10,6 +10,7 @@
namespace yii\util;
use yii\base\Exception;
use yii\base\InvalidConfigException;
/**
* Filesystem helper
......@@ -37,7 +38,7 @@ class FileHelper
* If the given path does not refer to an existing directory, an exception will be thrown.
* @param string $path the given path. This can also be a path alias.
* @return string the normalized path
* @throws Exception if the path does not refer to an existing directory.
* @throws InvalidConfigException if the path does not refer to an existing directory.
*/
public static function ensureDirectory($path)
{
......@@ -45,11 +46,25 @@ class FileHelper
if ($p !== false && ($p = realpath($p)) !== false && is_dir($p)) {
return $p;
} else {
throw new Exception('Directory does not exist: ' . $path);
throw new InvalidConfigException('Directory does not exist: ' . $path);
}
}
/**
* Normalizes a file/directory path.
* After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`,
* and any trailing directory separators will be removed. For example, '/home\demo/' on Linux
* will be normalized as '/home/demo'.
* @param string $path the file/directory path to be normalized
* @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`.
* @return string the normalized file/directory path
*/
public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)
{
return rtrim(strtr($path, array('/' => $ds, '\\' => $ds)), $ds);
}
/**
* Returns the localized version of a specified file.
*
* The searching is based on the specified language code. In particular,
......
<?php
/**
* ReflectionHelper class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\util;
use yii\base\Exception;
/**
* ReflectionHelper
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ReflectionHelper
{
/**
* Prepares parameters so that they can be bound to the specified method.
* This method converts the input parameters into an array that can later be
* passed to `call_user_func_array()` when calling the specified method.
* The conversion is based on the matching of method parameter names
* and the input array keys. For example,
*
* ~~~
* class Foo {
* function bar($a, $b) { ... }
* }
* $object = new Foo;
* $params = array('b' => 2, 'c' => 3, 'a' => 1);
* var_export(ReflectionHelper::extractMethodParams($object, 'bar', $params));
* // output: array('a' => 1, 'b' => 2);
* ~~~
*
* @param object|string $object the object or class name that owns the specified method
* @param string $method the method name
* @param array $params the parameters in terms of name-value pairs
* @return array parameters that are needed by the method only and
* can be passed to the method via `call_user_func_array()`.
* @throws Exception if any required method parameter is not found in the given parameters
*/
public static function extractMethodParams($object, $method, $params)
{
$m = new \ReflectionMethod($object, $method);
$ps = array();
foreach ($m->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
$ps[$name] = $params[$name];
} elseif ($param->isDefaultValueAvailable()) {
$ps[$name] = $param->getDefaultValue();
} else {
throw new Exception(\Yii::t('yii', 'Missing required parameter "{name}".', array('{name}' => $name)));
}
}
return $ps;
}
/**
* Initializes an object with the given parameters.
* Only the public non-static properties of the object will be initialized, and their names must
* match the given parameter names. For example,
*
* ~~~
* class Foo {
* public $a;
* protected $b;
* }
* $object = new Foo;
* $params = array('b' => 2, 'c' => 3, 'a' => 1);
* $remaining = ReflectionHelper::bindObjectParams($object, $params);
* var_export($object); // output: $object->a = 1; $object->b = null;
* var_export($remaining); // output: array('b' => 2, 'c' => 3);
* ~~~
*
* @param object $object the object whose properties are to be initialized
* @param array $params the input parameters to be used to initialize the object
* @return array the remaining unused input parameters
*/
public static function initObjectWithParams($object, $params)
{
if (empty($params)) {
return array();
}
$class = new \ReflectionClass(get_class($object));
foreach ($params as $name => $value) {
if ($class->hasProperty($name)) {
$property = $class->getProperty($name);
if ($property->isPublic() && !$property->isStatic()) {
$object->$name = $value;
unset($params[$name]);
}
}
}
return $params;
}
}
<?php
define('YII_DEBUG', true);
/**
* Yii console bootstrap file.
*
......@@ -8,16 +7,17 @@ define('YII_DEBUG', true);
* @license http://www.yiiframework.com/license/
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
require(__DIR__ . '/yii.php');
$config = array(
'controllerPath' => '@yii/console/controllers',
);
$id = 'yiic';
$basePath = __DIR__ . '/console';
$application = new yii\console\Application($id, $basePath, $config);
$application = new yii\console\Application($id, $basePath, array(
'controllerPath' => '@yii/console/controllers',
));
$application->run();
<?php
define('YII_ENABLE_EXCEPTION_HANDLER', false);
define('YII_ENABLE_ERROR_HANDLER', false);
define('YII_DEBUG', true);
$_SERVER['SCRIPT_NAME'] = '/' . __DIR__;
......
- db
* pgsql, sql server, oracle, db2 drivers
* write a guide on creating own schema definitions
* unit tests on different DB drivers
* document-based (should allow storage-specific methods additionally to generic ones)
* mongodb (put it under framework/db/mongodb)
* key-value-based (should allow storage-specific methods additionally to generic ones)
......@@ -8,8 +8,10 @@
- logging
* WebTarget (TBD after web is in place): should consider using javascript and make it into a toolbar
* ProfileTarget (TBD after web is in place): should consider using javascript and make it into a toolbar
* unit tests
- caching
* a console command to clear cached data
* unit tests
- validators
* FileValidator: depends on CUploadedFile
* CaptchaValidator: depends on CaptchaAction
......
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