Commit 6d2e0ba6 by Qiang Xue

w

parent 43777054
...@@ -160,6 +160,9 @@ class YiiBase ...@@ -160,6 +160,9 @@ class YiiBase
* In the latter case, the root alias will be replaced by the corresponding registered path * In the latter case, the root alias will be replaced by the corresponding registered path
* and the remaining part will be appended to it. * and the remaining part will be appended to it.
* *
* In case the given alias is not an alias (i.e., not starting with '@'),
* it will be returned back as is.
*
* Note, this method does not ensure the existence of the resulting path. * Note, this method does not ensure the existence of the resulting path.
* @param string $alias alias * @param string $alias alias
* @return mixed path corresponding to the alias, false if the root alias is not previously registered. * @return mixed path corresponding to the alias, false if the root alias is not previously registered.
...@@ -175,6 +178,9 @@ class YiiBase ...@@ -175,6 +178,9 @@ class YiiBase
if (isset(self::$aliases[$rootAlias])) { if (isset(self::$aliases[$rootAlias])) {
return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos); return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
} }
else if($alias[0] !== '@') { // not an alias
return $alias;
}
} }
return false; return false;
} }
...@@ -344,7 +350,6 @@ class YiiBase ...@@ -344,7 +350,6 @@ class YiiBase
} }
if ($object instanceof Initable) { if ($object instanceof Initable) {
$object->preinit();
foreach ($config as $name => $value) { foreach ($config as $name => $value) {
$object->$name = $value; $object->$name = $value;
} }
......
...@@ -307,9 +307,9 @@ class Component ...@@ -307,9 +307,9 @@ class Component
* Creates a new component instance. * Creates a new component instance.
* *
* This method differs from the PHP `new` operator in that it does the following * This method differs from the PHP `new` operator in that it does the following
* additional work after the component is created: * steps to create a new component instance:
* *
* - Call [[Initable::preinit|preinit]] if the class implements [[Initable]]; * - Call class constructor (same the `new` operator);
* - Initialize the component properties using the name-value pairs given as the * - Initialize the component properties using the name-value pairs given as the
* last parameter to this method; * last parameter to this method;
* - Call [[Initable::init|init]] if the class implements [[Initable]]. * - Call [[Initable::init|init]] if the class implements [[Initable]].
...@@ -329,7 +329,6 @@ class Component ...@@ -329,7 +329,6 @@ class Component
* $model = Foo::create(1, 2, array('c' => 3)); * $model = Foo::create(1, 2, array('c' => 3));
* // which is equivalent to the following lines: * // which is equivalent to the following lines:
* $model = new Foo(1, 2); * $model = new Foo(1, 2);
* $model->preinit();
* $model->c = 3; * $model->c = 3;
* $model->init(); * $model->init();
* ~~~ * ~~~
......
...@@ -13,9 +13,9 @@ namespace yii\base; ...@@ -13,9 +13,9 @@ namespace yii\base;
* Initable is an interface indicating a class needs initialization to work properly. * Initable is an interface indicating a class needs initialization to work properly.
* *
* Initable requires a class to implement the [[init]] method. * Initable requires a class to implement the [[init]] method.
* When [[\Yii::createComponent]] is creating a new component instance, if the component * When [[\Yii::createComponent]] is being used to create a new component which implements
* class implements Initable interface, the method will call its [[init]] method * Initable, it will call the [[init]] method after setting the initial values of the
* after setting the initial values of the component properties. * component properties.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
...@@ -23,23 +23,10 @@ namespace yii\base; ...@@ -23,23 +23,10 @@ namespace yii\base;
interface Initable interface Initable
{ {
/** /**
* Pre-initializes this component.
* This method is invoked by [[\Yii::createComponent]] after its creates the new
* component instance, but BEFORE the component properties are initialized.
*
* You may implement this method to do work such as setting property default values.
*/
public function preinit();
/**
* Initializes this component. * Initializes this component.
* This method is invoked by [[\Yii::createComponent]] after its creates the new * This method is invoked by [[\Yii::createComponent]] after its creates the new
* component instance and initializes the component properties. In other words, * component instance and initializes the component properties. In other words,
* at this stage, the component has been fully configured. * at this stage, the component has been fully configured.
*
* The default implementation calls [[behaviors]] and registers any available behaviors.
* You may override this method with additional initialization logic (e.g. establish DB connection).
* Make sure you call the parent implementation.
*/ */
public function init(); public function init();
} }
...@@ -50,18 +50,6 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc ...@@ -50,18 +50,6 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
} }
/** /**
* Pre-initializes this model.
* This method is required by the [[Initable]] interface. It is invoked by
* [[\Yii::createComponent]] after its creates the new model instance but
* BEFORE the model properties are initialized.
*
* You may override this method to do work such as setting property default values.
*/
public function preinit()
{
}
/**
* Initializes this model. * Initializes this model.
* *
* This method is required by the [[Initable]] interface. It is invoked by [[\Yii::createComponent]] * This method is required by the [[Initable]] interface. It is invoked by [[\Yii::createComponent]]
......
<?php <?php
/** /**
* CEmailLogRoute class file. * EmailTarget class file.
* *
* @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 &copy; 2008-2011 Yii Software LLC * @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\logging;
/** /**
* CEmailLogRoute sends selected log messages to email addresses. * EmailTarget sends selected log messages to the specified email addresses.
* *
* The target email addresses may be specified via {@link setEmails emails} property. * The target email addresses may be specified via [[emails]] property.
* Optionally, you may set the email {@link setSubject subject}, the * Optionally, you may set the email [[subject]], [[sentFrom]] address and
* {@link setSentFrom sentFrom} address and any additional {@link setHeaders headers}. * additional [[headers]].
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CEmailLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $ * @since 2.0
* @package system.logging
* @since 1.0
*/ */
class CEmailLogRoute extends CLogRoute class EmailTarget extends Target
{ {
/** /**
* @var array list of destination email addresses. * @var array list of destination email addresses.
*/ */
private $_email = array(); public $emails = array();
/** /**
* @var string email subject * @var string email subject
*/ */
private $_subject; public $subject;
/** /**
* @var string email sent from address * @var string email sent-from address
*/ */
private $_from; public $sentFrom;
/** /**
* @var array list of additional headers to use when sending an email. * @var array list of additional headers to use when sending an email.
*/ */
private $_headers = array(); public $headers = array();
/** /**
* Sends log messages to specified email addresses. * Sends log [[messages]] to specified email addresses.
* @param array $logs list of log messages * @param boolean $final whether this method is called at the end of the current application
*/ */
protected function processLogs($logs) public function exportMessages($final)
{ {
$message = ''; $body = '';
foreach ($logs as $log) foreach ($this->messages as $message) {
$message .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]); $body .= $this->formatMessage($message);
$message = wordwrap($message, 70); }
$subject = $this->getSubject(); $body = wordwrap($body, 70);
if ($subject === null) $subject = $this->subject === null ? Yii::t('yii', 'Application Log') : $this->subject;
$subject = Yii::t('yii', 'Application Log'); foreach ($this->emails as $email) {
foreach ($this->getEmails() as $email) $this->sendEmail($subject, $body, $email, $this->sentFrom, $this->headers);
$this->sendEmail($email, $subject, $message); }
$this->messages = array();
} }
/** /**
* Sends an email. * Sends an email.
* @param string $email single email address
* @param string $subject email subject * @param string $subject email subject
* @param string $message email content * @param string $body email body
*/ * @param string $sentTo sent-to email address
protected function sendEmail($email, $subject, $message) * @param string $sentFrom sent-from email address
{ * @param array $headers additional headers to be used when sending the email
$headers = $this->getHeaders();
if (($from = $this->getSentFrom()) !== null)
$headers[] = "From: {$from}";
mail($email, $subject, $message, implode("\r\n", $headers));
}
/**
* @return array list of destination email addresses
*/
public function getEmails()
{
return $this->_email;
}
/**
* @param mixed $value list of destination email addresses. If the value is
* a string, it is assumed to be comma-separated email addresses.
*/
public function setEmails($value)
{
if (is_array($value))
$this->_email = $value;
else
$this->_email = preg_split('/[\s,]+/', $value, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* @return string email subject. Defaults to CEmailLogRoute::DEFAULT_SUBJECT
*/
public function getSubject()
{
return $this->_subject;
}
/**
* @param string $value email subject.
*/
public function setSubject($value)
{
$this->_subject = $value;
}
/**
* @return string send from address of the email
*/
public function getSentFrom()
{
return $this->_from;
}
/**
* @param string $value send from address of the email
*/
public function setSentFrom($value)
{
$this->_from = $value;
}
/**
* @return array additional headers to use when sending an email.
* @since 1.1.4
*/
public function getHeaders()
{
return $this->_headers;
}
/**
* @param mixed $value list of additional headers to use when sending an email.
* If the value is a string, it is assumed to be line break separated headers.
* @since 1.1.4
*/ */
public function setHeaders($value) protected function sendEmail($subject, $body, $sentTo, $sentFrom, $headers)
{ {
if (is_array($value)) if ($sentFrom !== null) {
$this->_headers = $value; $headers[] = "From: {$sentFrom}";
else }
$this->_headers = preg_split('/\r\n|\n/', $value, -1, PREG_SPLIT_NO_EMPTY); mail($email, $subject, $body, implode("\r\n", $headers));
} }
} }
\ No newline at end of file
<?php
/**
* CLogFilter class file
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CLogFilter preprocesses the logged messages before they are handled by a log route.
*
* CLogFilter is meant to be used by a log route to preprocess the logged messages
* before they are handled by the route. The default implementation of CLogFilter
* prepends additional context information to the logged messages. In particular,
* by setting {@link logVars}, predefined PHP variables such as
* $_SERVER, $_POST, etc. can be saved as a log message, which may help identify/debug
* issues encountered.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CLogFilter.php 3204 2011-05-05 21:36:32Z alexander.makarow $
* @package system.logging
* @since 1.0.6
*/
class CLogFilter extends CComponent
{
/**
* @var boolean whether to prefix each log message with the current user session ID.
* Defaults to false.
*/
public $prefixSession = false;
/**
* @var boolean whether to prefix each log message with the current user
* {@link CWebUser::name name} and {@link CWebUser::id ID}. Defaults to false.
*/
public $prefixUser = false;
/**
* @var boolean whether to log the current user name and ID. Defaults to true.
*/
public $logUser = true;
/**
* @var array list of the PHP predefined variables that should be logged.
* Note that a variable must be accessible via $GLOBALS. Otherwise it won't be logged.
*/
public $logVars = array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER');
/**
* Filters the given log messages.
* This is the main method of CLogFilter. It processes the log messages
* by adding context information, etc.
* @param array $logs the log messages
* @return array
*/
public function filter(&$logs)
{
if (!empty($logs))
{
if (($message = $this->getContext()) !== '')
array_unshift($logs, array($message, CLogger::LEVEL_INFO, 'application', YII_BEGIN_TIME));
$this->format($logs);
}
return $logs;
}
/**
* Formats the log messages.
* The default implementation will prefix each message with session ID
* if {@link prefixSession} is set true. It may also prefix each message
* with the current user's name and ID if {@link prefixUser} is true.
* @param array $logs the log messages
*/
protected function format(&$logs)
{
$prefix = '';
if ($this->prefixSession && ($id = session_id()) !== '')
$prefix .= "[$id]";
if ($this->prefixUser && ($user = Yii::app()->getComponent('user', false)) !== null)
$prefix .= '[' . $user->getName() . '][' . $user->getId() . ']';
if ($prefix !== '')
{
foreach ($logs as &$log)
$log[0] = $prefix . ' ' . $log[0];
}
}
/**
* Generates the context information to be logged.
* The default implementation will dump user information, system variables, etc.
* @return string the context information. If an empty string, it means no context information.
*/
protected function getContext()
{
$context = array();
if ($this->logUser && ($user = Yii::app()->getComponent('user', false)) !== null)
$context[] = 'User: ' . $user->getName() . ' (ID: ' . $user->getId() . ')';
foreach ($this->logVars as $name)
{
if (!empty($GLOBALS[$name]))
$context[] = "\$ {$name}=" . var_export($GLOBALS[$name], true);
}
return implode("\n\n", $context);
}
}
\ No newline at end of file
...@@ -38,20 +38,26 @@ class Logger extends \yii\base\Component ...@@ -38,20 +38,26 @@ class Logger extends \yii\base\Component
public $flushInterval = 1000; public $flushInterval = 1000;
/** /**
* @var boolean this property will be passed as the parameter to [[flush]] when it is * @var boolean this property will be passed as the parameter to [[flush]] when it is
* called due to the [[flushInterval]] is reached. Defaults to false, meaning the flushed * called due to the [[flushInterval]] is reached. Defaults to true, meaning the flushed
* messages are still kept in the memory by each log target. If this is true, they will * messages will be exported to the actual storage medium (e.g. DB, email) defined by each
* be exported to the actual storage medium (e.g. DB, email) defined by each log target. * log target. If false, the flushed messages will be kept in the memory of each log target.
* @see flushInterval * @see flushInterval
*/ */
public $autoExport = false; public $autoExport = true;
/** /**
* @var array logged messages. This property is mainly managed by [[log]] and [[flush]]. * @var array logged messages. This property is mainly managed by [[log]] and [[flush]].
* Each log message is of the following structure:
*
* ~~~
* array(
* [0] => message (string)
* [1] => level (string)
* [2] => category (string)
* [3] => timestamp (float, obtained by microtime(true))
* )
* ~~~
*/ */
public $messages = array(); public $messages = array();
/**
* @var array the profiling results (category, token => time in seconds)
*/
private $_timings;
/** /**
* Logs an error message. * Logs an error message.
...@@ -155,72 +161,24 @@ class Logger extends \yii\base\Component ...@@ -155,72 +161,24 @@ class Logger extends \yii\base\Component
} }
/** /**
* Retrieves log messages. * Removes all recorded messages from the memory.
* * This method will raise an {@link onFlush} event.
* Messages may be filtered by log levels and/or categories. * The attached event handlers can process the log messages before they are removed.
* A level filter is specified by a list of levels separated by comma or space * @param boolean $export whether to notify log targets to export the filtered messages they have received.
* (e.g. 'trace, error'). A category filter is similar to level filter
* (e.g. 'system, system.web'). A difference is that in category filter
* you can use pattern like 'system.*' to indicate all categories starting
* with 'system'.
*
* If you do not specify level filter, it will bring back logs at all levels.
* The same applies to category filter.
*
* Level filter and category filter are combinational, i.e., only messages
* satisfying both filter conditions will be returned.
*
* @param string $levels level filter
* @param string $categories category filter
* @return array list of messages. Each array elements represents one message
* with the following structure:
* array(
* [0] => message (string)
* [1] => level (string)
* [2] => category (string)
* [3] => timestamp (float, obtained by microtime(true));
*/
public function getLogs($levels = '', $categories = '')
{
$this->_levels = preg_split('/[\s,]+/', strtolower($levels), -1, PREG_SPLIT_NO_EMPTY);
$this->_categories = preg_split('/[\s,]+/', strtolower($categories), -1, PREG_SPLIT_NO_EMPTY);
if (empty($levels) && empty($categories))
return $this->_logs;
elseif (empty($levels))
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByCategory'))));
elseif (empty($categories))
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel'))));
else
{
$ret = array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel'))));
return array_values(array_filter(array_filter($ret, array($this, 'filterByCategory'))));
}
}
/**
* Filter function used by {@link getLogs}
* @param array $value element to be filtered
* @return array valid log, false if not.
*/ */
private function filterByCategory($value) public function flush($export = false)
{ {
foreach ($this->_categories as $category) $this->onFlush(new \yii\base\Event($this, array('export' => $export, 'flush' => true)));
{ $this->messages = array();
$cat = strtolower($value[2]);
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0))
return $value;
}
return false;
} }
/** /**
* Filter function used by {@link getLogs} * Raises an `onFlush` event.
* @param array $value element to be filtered * @param \yii\base\Event $event the event parameter
* @return array valid log, false if not.
*/ */
private function filterByLevel($value) public function onFlush($event)
{ {
return in_array(strtolower($value[1]), $this->_levels) ? $value : false; $this->raiseEvent('onFlush', $event);
} }
/** /**
...@@ -237,36 +195,58 @@ class Logger extends \yii\base\Component ...@@ -237,36 +195,58 @@ class Logger extends \yii\base\Component
/** /**
* Returns the profiling results. * Returns the profiling results.
* The results may be filtered by token and/or category. *
* If no filter is specified, the returned results would be an array with each element * By default, all profiling results will be returned. You may provide
* being `array($token, $category, $time)`. * `$categories` and `$excludeCategories` as parameters to retrieve the
* If a filter is specified, the results would be an array of timings. * results that you are interested in.
* @param string $token token filter. Defaults to null, meaning not filtered by token. *
* @param string $category category filter. Defaults to null, meaning not filtered by category. * @param array $categories list of categories that you are interested in.
* @param boolean $refresh whether to refresh the internal timing calculations. If false, * You can use an asterisk at the end of a category to do a prefix match.
* only the first time calling this method will the timings be calculated internally. * For example, 'yii\db\*' will match categories starting with 'yii\db\',
* @return array the profiling results. * such as 'yii\db\dao\Connection'.
* @param array $excludeCategories list of categories that you are interested in.
* @return array the profiling results. Each array element has the following structure:
* `array($category, $time)`.
*/ */
public function getProfilingResults($token = null, $category = null, $refresh = false) public function getProfiling($categories = array(), $excludeCategories = array())
{ {
if ($this->_timings === null || $refresh) { $timings = $this->calculateTimings();
$this->calculateTimings(); if (empty($categories) && empty($excludeCategories)) {
} return $timings;
if ($token === null && $category === null) {
return $this->_timings;
} }
$results = array();
foreach ($this->_timings as $timing) { foreach ($timings as $i => $timing) {
if (($category === null || $timing[1] === $category) && ($token === null || $timing[0] === $token)) { $matched = empty($categories);
$results[] = $timing[2]; foreach ($categories as $category) {
$prefix = rtrim($category, '*');
if (strpos($timing[0], $prefix) === 0 && ($timing[0] === $category || $prefix !== $category)) {
$matched = true;
break;
}
}
if ($matched) {
foreach ($excludeCategories as $category) {
$prefix = rtrim($category, '*');
foreach ($timings as $i => $timing) {
if (strpos($timing[0], $prefix) === 0 && ($timing[0] === $category || $prefix !== $category)) {
$matched = false;
break;
}
}
}
}
if (!$matched) {
unset($timings[$i]);
} }
} }
return $results; return array_values($timings);
} }
private function calculateTimings() private function calculateTimings()
{ {
$this->_timings = array(); $timings = array();
$stack = array(); $stack = array();
foreach ($this->messages as $log) { foreach ($this->messages as $log) {
...@@ -274,18 +254,16 @@ class Logger extends \yii\base\Component ...@@ -274,18 +254,16 @@ class Logger extends \yii\base\Component
continue; continue;
} }
list($message, $level, $category, $timestamp) = $log; list($message, $level, $category, $timestamp) = $log;
if (!strncasecmp($message, 'begin:', 6)) { if ($message === 'begin') {
$log[0] = substr($message, 6);
$stack[] = $log; $stack[] = $log;
} }
elseif (!strncasecmp($message, 'end:', 4)) { else { // $message === 'end'
$token = substr($message, 4); if (($last = array_pop($stack)) !== null && $last[2] === $category) {
if (($last = array_pop($stack)) !== null && $last[0] === $token) { $delta = $timestamp - $last[3];
$delta = $log[3] - $last[3]; $timings[] = array($category, $delta);
$this->_timings[] = array($message, $category, $delta);
} }
else { else {
throw new \yii\base\Exception('Found a mismatching profiling block: ' . $token); throw new \yii\base\Exception('Found a mismatching profiling block: ' . $category);
} }
} }
} }
...@@ -293,28 +271,10 @@ class Logger extends \yii\base\Component ...@@ -293,28 +271,10 @@ class Logger extends \yii\base\Component
$now = microtime(true); $now = microtime(true);
while (($last = array_pop($stack)) !== null) { while (($last = array_pop($stack)) !== null) {
$delta = $now - $last[3]; $delta = $now - $last[3];
$this->_timings[] = array($last[0], $last[2], $delta); $timings[] = array($last[2], $delta);
} }
}
/** return $timings;
* Removes all recorded messages from the memory.
* This method will raise an {@link onFlush} event.
* The attached event handlers can process the log messages before they are removed.
* @param boolean $export whether to notify log targets to export the filtered messages they have received.
*/
public function flush($export = false)
{
$this->onFlush(new \yii\base\Event($this, array('export' => $export)));
$this->messages = array();
} }
/**
* Raises an `onFlush` event.
* @param \yii\base\Event $event the event parameter
*/
public function onFlush($event)
{
$this->raiseEvent('onFlush', $event);
}
} }
...@@ -125,11 +125,12 @@ class Router extends \yii\base\ApplicationComponent ...@@ -125,11 +125,12 @@ class Router extends \yii\base\ApplicationComponent
*/ */
public function processMessages($event) public function processMessages($event)
{ {
$logger = Yii::getLogger(); $messages = \Yii::getLogger()->messages;
$export = !isset($event->params['export']) || $event->params['export']; $export = !isset($event->params['export']) || $event->params['export'];
$final = !isset($event-params['flush']) || !$event->params['flush'];
foreach ($this->_targets as $target) { foreach ($this->_targets as $target) {
if ($target->enabled) { if ($target->enabled) {
$target->processMessages($logger, $export); $target->processMessages($messages, $export, $final);
} }
} }
} }
......
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