Commit 6d2e0ba6 by Qiang Xue

w

parent 43777054
......@@ -160,6 +160,9 @@ class YiiBase
* In the latter case, the root alias will be replaced by the corresponding registered path
* 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.
* @param string $alias alias
* @return mixed path corresponding to the alias, false if the root alias is not previously registered.
......@@ -175,6 +178,9 @@ class YiiBase
if (isset(self::$aliases[$rootAlias])) {
return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
}
else if($alias[0] !== '@') { // not an alias
return $alias;
}
}
return false;
}
......@@ -344,7 +350,6 @@ class YiiBase
}
if ($object instanceof Initable) {
$object->preinit();
foreach ($config as $name => $value) {
$object->$name = $value;
}
......
......@@ -307,9 +307,9 @@ class Component
* Creates a new component instance.
*
* 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
* last parameter to this method;
* - Call [[Initable::init|init]] if the class implements [[Initable]].
......@@ -329,7 +329,6 @@ class Component
* $model = Foo::create(1, 2, array('c' => 3));
* // which is equivalent to the following lines:
* $model = new Foo(1, 2);
* $model->preinit();
* $model->c = 3;
* $model->init();
* ~~~
......
......@@ -13,9 +13,9 @@ namespace yii\base;
* Initable is an interface indicating a class needs initialization to work properly.
*
* Initable requires a class to implement the [[init]] method.
* When [[\Yii::createComponent]] is creating a new component instance, if the component
* class implements Initable interface, the method will call its [[init]] method
* after setting the initial values of the component properties.
* When [[\Yii::createComponent]] is being used to create a new component which implements
* Initable, it will call the [[init]] method after setting the initial values of the
* component properties.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -23,23 +23,10 @@ namespace yii\base;
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.
* This method is invoked by [[\Yii::createComponent]] after its creates the new
* component instance and initializes the component properties. In other words,
* 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();
}
......@@ -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.
*
* This method is required by the [[Initable]] interface. It is invoked by [[\Yii::createComponent]]
......
<?php
/**
* CEmailLogRoute class file.
* EmailTarget class file.
*
* @author Qiang Xue <qiang.xue@gmail.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/
*/
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.
* Optionally, you may set the email {@link setSubject subject}, the
* {@link setSentFrom sentFrom} address and any additional {@link setHeaders headers}.
* The target email addresses may be specified via [[emails]] property.
* Optionally, you may set the email [[subject]], [[sentFrom]] address and
* additional [[headers]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CEmailLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $
* @package system.logging
* @since 1.0
* @since 2.0
*/
class CEmailLogRoute extends CLogRoute
class EmailTarget extends Target
{
/**
* @var array list of destination email addresses.
*/
private $_email = array();
public $emails = array();
/**
* @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.
*/
private $_headers = array();
public $headers = array();
/**
* Sends log messages to specified email addresses.
* @param array $logs list of log messages
* Sends log [[messages]] to specified email addresses.
* @param boolean $final whether this method is called at the end of the current application
*/
protected function processLogs($logs)
public function exportMessages($final)
{
$message = '';
foreach ($logs as $log)
$message .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]);
$message = wordwrap($message, 70);
$subject = $this->getSubject();
if ($subject === null)
$subject = Yii::t('yii', 'Application Log');
foreach ($this->getEmails() as $email)
$this->sendEmail($email, $subject, $message);
$body = '';
foreach ($this->messages as $message) {
$body .= $this->formatMessage($message);
}
/**
* Sends an email.
* @param string $email single email address
* @param string $subject email subject
* @param string $message email content
*/
protected function sendEmail($email, $subject, $message)
{
$headers = $this->getHeaders();
if (($from = $this->getSentFrom()) !== null)
$headers[] = "From: {$from}";
mail($email, $subject, $message, implode("\r\n", $headers));
$body = wordwrap($body, 70);
$subject = $this->subject === null ? Yii::t('yii', 'Application Log') : $this->subject;
foreach ($this->emails as $email) {
$this->sendEmail($subject, $body, $email, $this->sentFrom, $this->headers);
}
/**
* @return array list of destination email addresses
*/
public function getEmails()
{
return $this->_email;
$this->messages = array();
}
/**
* @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
* Sends an email.
* @param string $subject email subject
* @param string $body email body
* @param string $sentTo sent-to email address
* @param string $sentFrom sent-from email address
* @param array $headers additional headers to be used when sending the email
*/
public function getHeaders()
protected function sendEmail($subject, $body, $sentTo, $sentFrom, $headers)
{
return $this->_headers;
if ($sentFrom !== null) {
$headers[] = "From: {$sentFrom}";
}
/**
* @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)
{
if (is_array($value))
$this->_headers = $value;
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
public $flushInterval = 1000;
/**
* @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
* messages are still kept in the memory by each log target. If this is true, they will
* be exported to the actual storage medium (e.g. DB, email) defined by each log target.
* called due to the [[flushInterval]] is reached. Defaults to true, meaning the flushed
* messages will be exported to the actual storage medium (e.g. DB, email) defined by each
* log target. If false, the flushed messages will be kept in the memory of each log target.
* @see flushInterval
*/
public $autoExport = false;
public $autoExport = true;
/**
* @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();
/**
* @var array the profiling results (category, token => time in seconds)
*/
private $_timings;
/**
* Logs an error message.
......@@ -155,72 +161,24 @@ class Logger extends \yii\base\Component
}
/**
* Retrieves log messages.
*
* Messages may be filtered by log levels and/or categories.
* A level filter is specified by a list of levels separated by comma or space
* (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.
* 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.
*/
private function filterByCategory($value)
{
foreach ($this->_categories as $category)
public function flush($export = false)
{
$cat = strtolower($value[2]);
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0))
return $value;
}
return false;
$this->onFlush(new \yii\base\Event($this, array('export' => $export, 'flush' => true)));
$this->messages = array();
}
/**
* Filter function used by {@link getLogs}
* @param array $value element to be filtered
* @return array valid log, false if not.
* Raises an `onFlush` event.
* @param \yii\base\Event $event the event parameter
*/
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
/**
* 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
* being `array($token, $category, $time)`.
* If a filter is specified, the results would be an array of timings.
* @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 boolean $refresh whether to refresh the internal timing calculations. If false,
* only the first time calling this method will the timings be calculated internally.
* @return array the profiling results.
*
* By default, all profiling results will be returned. You may provide
* `$categories` and `$excludeCategories` as parameters to retrieve the
* results that you are interested in.
*
* @param array $categories list of categories that you are interested in.
* You can use an asterisk at the end of a category to do a prefix match.
* For example, 'yii\db\*' will match categories starting with 'yii\db\',
* 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) {
$this->calculateTimings();
$timings = $this->calculateTimings();
if (empty($categories) && empty($excludeCategories)) {
return $timings;
}
foreach ($timings as $i => $timing) {
$matched = empty($categories);
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 ($token === null && $category === null) {
return $this->_timings;
}
$results = array();
foreach ($this->_timings as $timing) {
if (($category === null || $timing[1] === $category) && ($token === null || $timing[0] === $token)) {
$results[] = $timing[2];
}
if (!$matched) {
unset($timings[$i]);
}
}
return $results;
return array_values($timings);
}
private function calculateTimings()
{
$this->_timings = array();
$timings = array();
$stack = array();
foreach ($this->messages as $log) {
......@@ -274,18 +254,16 @@ class Logger extends \yii\base\Component
continue;
}
list($message, $level, $category, $timestamp) = $log;
if (!strncasecmp($message, 'begin:', 6)) {
$log[0] = substr($message, 6);
if ($message === 'begin') {
$stack[] = $log;
}
elseif (!strncasecmp($message, 'end:', 4)) {
$token = substr($message, 4);
if (($last = array_pop($stack)) !== null && $last[0] === $token) {
$delta = $log[3] - $last[3];
$this->_timings[] = array($message, $category, $delta);
else { // $message === 'end'
if (($last = array_pop($stack)) !== null && $last[2] === $category) {
$delta = $timestamp - $last[3];
$timings[] = array($category, $delta);
}
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
$now = microtime(true);
while (($last = array_pop($stack)) !== null) {
$delta = $now - $last[3];
$this->_timings[] = array($last[0], $last[2], $delta);
}
$timings[] = array($last[2], $delta);
}
/**
* 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();
return $timings;
}
/**
* 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
*/
public function processMessages($event)
{
$logger = Yii::getLogger();
$messages = \Yii::getLogger()->messages;
$export = !isset($event->params['export']) || $event->params['export'];
$final = !isset($event-params['flush']) || !$event->params['flush'];
foreach ($this->_targets as $target) {
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