Current Directory: /home/astoriaah/www/administrator/components/com_admintools/Model/Scanner/Logger
Viewing File: /home/astoriaah/www/administrator/components/com_admintools/Model/Scanner/Logger/Logger.php
<?php
/**
* @package admintools
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\AdminTools\Admin\Model\Scanner\Logger;
defined('_JEXEC') || die;
use Akeeba\AdminTools\Admin\Model\Scanner\Mixin\Singleton;
use Akeeba\AdminTools\Admin\Model\Scanner\Util\Configuration;
use Akeeba\AdminTools\Admin\Model\Scanner\Util\Filesystem;
use FOF40\Container\Container;
class Logger
{
use Singleton;
/**
* Full path to log file
*
* @var string
*/
protected $logName = null;
/**
* The file pointer to the current log file
*
* @var resource
*/
protected $fp = null;
/**
* The minimum log level
*
* @var int
*/
protected $configuredLoglevel;
/**
* The raw path to the site's root
*
* @var string
*/
protected $siteRoot;
/**
* The normalized path to the site's root
*
* @var string
*/
protected $normalizedSiteRoot;
/**
* The warnings in the current queue
*
* @var string[]
*/
private $warningsQueue = [];
/**
* The maximum length of the warnings queue
*
* @var int
*/
private $warningsQueueSize = 0;
/**
* The scanner configuration
*
* @var Configuration
*/
private $configuration;
/**
* Public constructor. Initialises the properties with the parameters from the backup profile and platform.
*/
public function __construct(Configuration $configuration)
{
$this->configuration = $configuration;
$this->initialiseWithProfileParameters();
}
/**
* When shutting down this class always close any open log files.
*/
public function __destruct()
{
$this->close();
}
/**
* Clears the logfile
*
* @return void
*/
public function reset()
{
// Get the file names for the default log and the tagged log
$currentLogName = $this->logName;
$this->logName = $this->getLogFilename();
// Close the file if it's open
if ($currentLogName == $this->logName)
{
$this->close();
}
// Remove the log file if it exists
@unlink($this->logName);
// Reset the log file
$fp = @fopen($this->logName, 'w');
if ($fp === false)
{
return;
}
fwrite($fp, '<?php die(); ?>' . "\n");
@fclose($fp);
}
/**
* Writes a line to the log, if the log level is high enough
*
* @param string $level The log level
* @param string $message The message to write to the log
* @param array $context The logging context. For PSR-3 compatibility but not used in text file logs.
*
* @return void
*/
public function log($level, $message = '', array $context = [])
{
// Warnings are enqueued no matter what is the minimum log level to report in the log file
if ($level == LogLevel::WARNING)
{
$this->enqueueWarning($message);
}
// If we are told to not log anything we can't continue
if ($this->configuredLoglevel == 0)
{
return;
}
// Open the log if it's closed
if (is_null($this->fp))
{
$this->open();
}
// If the log could not be opened we can't continue
if (is_null($this->fp))
{
return;
}
// If the minimum log level is lower than what we're trying to log we cannot continue
if ($this->configuredLoglevel < $level)
{
return;
}
// Replace the site's root with <root> in the log file. Replace newlines with the string literal ' \n '
$message = strtr($message, [
$this->siteRoot => "<root>",
$this->normalizedSiteRoot => "<root>",
"\r\n" => ' \\n ',
"\r" => ' \\n ',
]);
switch ($level)
{
case LogLevel::ERROR:
$string = "ERROR |";
break;
case LogLevel::WARNING:
$string = "WARNING |";
break;
case LogLevel::INFO:
$string = "INFO |";
break;
case LogLevel::DEBUG:
default:
$string = "DEBUG |";
break;
}
$string .= gmdate('Ymd H:i:s') . "|$message\r\n";
@fwrite($this->fp, $string);
}
/**
* Calculates the absolute path to the log file
*
* @return string The absolute path to the log file
*/
public function getLogFilename()
{
$container = Container::getInstance('com_admintools');
$tmpPath = $container->platform->getConfig()->get('tmp_path', JPATH_ROOT);
$logPath = $container->platform->getConfig()->get('log_path', $tmpPath);
$logFilePath = $logPath . '/admintools_filescanner.php';
return Filesystem::normalizePath($logFilePath);
}
/**
* Close the currently active log and set the current tag to null.
*
* @return void
*/
public function close()
{
// The log file changed. Close the old log.
if (is_resource($this->fp))
{
@fclose($this->fp);
}
$this->fp = null;
}
/**
* Open a new log instance with the specified tag. If another log is already open it is closed before switching to
* the new log tag. If the tag is null use the default log defined in the logging system.
*
* @return void
*/
public function open()
{
// If the log is already open do nothing
if (is_resource($this->fp))
{
return;
}
// Re-initialise site root and minimum log level since the active profile might have changed in the meantime
$this->initialiseWithProfileParameters();
// Get the log filename
$this->logName = $this->getLogFilename();
// Touch the file
@touch($this->logName);
// Open the log file
$this->fp = @fopen($this->logName, 'a');
// If we couldn't open the file set the file pointer to null
if ($this->fp === false)
{
$this->fp = null;
}
}
/**
* The process failed with an error. We cannot proceed beyond that point.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = [])
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* An error which does not prevent the process from continuing. However, the condition must be reported back to the
* user.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = [])
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Interesting events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = [])
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = [])
{
$this->log(LogLevel::DEBUG, $message, $context);
}
/**
* A combination of getWarnings() and resetWarnings(). Returns the warnings and immediately resets the warnings
* queue.
*
* @return array
*/
final public function getAndResetWarnings()
{
$ret = $this->getWarnings();
$this->resetWarnings();
return $ret;
}
/**
* Returns an array with all warnings logged since the last time warnings were reset. The maximum number of warnings
* returned is controlled by setWarningsQueueSize().
*
* @return array
*/
final public function getWarnings()
{
return $this->warningsQueue;
}
/**
* Resets the warnings queue.
*
* @return void
*/
final public function resetWarnings()
{
$this->warningsQueue = [];
}
/**
* Returns the warnings queue size.
*
* @return int
*/
final public function getWarningsQueueSize()
{
return $this->warningsQueueSize;
}
/**
* Set the warnings queue size. A size of 0 means "no limit".
*
* @param int $queueSize The size of the warnings queue (in number of warnings items)
*
* @return void
*/
final public function setWarningsQueueSize($queueSize = 0)
{
if (!is_numeric($queueSize) || empty($queueSize) || ($queueSize < 0))
{
$queueSize = 0;
}
$this->warningsQueueSize = $queueSize;
}
/**
* Initialise the logger properties with parameters from the backup profile and the platform
*
* @return void
*/
protected function initialiseWithProfileParameters()
{
$this->siteRoot = JPATH_ROOT;
$this->normalizedSiteRoot = Filesystem::normalizePath($this->siteRoot);
$this->configuredLoglevel = (int) $this->configuration->get('logLevel');
}
/**
* Adds a warning to the warnings queue.
*
* @param string $warning
*/
final protected function enqueueWarning($warning)
{
$this->warningsQueue[] = $warning;
// If there is no queue size limit there's nothing else to be done.
if ($this->warningsQueueSize <= 0)
{
return;
}
// If the queue size is exceeded remove as many of the earliest elements as required
if (count($this->warningsQueue) > $this->warningsQueueSize)
{
$this->warningsQueueSize = array_slice($this->warningsQueue, -$this->warningsQueueSize);
}
}
}