<?php
/**
* StreamLoggerTrait file
*
* Copyright (c) 2016, Kiril Savchev
* All rights reserved.
*
* @category Libs
* @package Logger
*
* @author Kiril Savchev <k.savchev@gmail.com>
*
* @license https://opensource.org/licenses/BSD-3-Clause BSD 3 License
* @link http://ifthenelse.info
*/
namespace Ite\Logger\Stream;
use Ite\Logger\AbstractDatabaseLogger;
use Ite\Logger\AbstractEmailLogger;
use Ite\Logger\Exception\InvalidArgumentException;
use Ite\Logger\FileLogger as FLogger;
use Ite\Logger\Stream\Strategy\FileLoggerStrategy;
use Psr\Log\LoggerInterface;
/**
* Trait with common stream loggers functionalities
*
* @version 1.1
*
* @author Kiril Savchev <k.savchev@gmail.com>
*/
trait StreamLoggerTrait {
/**
* The logger
*
* @var LoggerInterface
*/
protected $logger;
/**
* The log level
*
* @var string
*/
protected $level;
/**
* Map with pre-preparation strategies for logger classes
*
* @var array
*/
protected $loggerStrategies = [
FLogger::class => FileLoggerStrategy::class,
AbstractEmailLogger::class => Strategy\AbstractEmailLoggerStrategy::class,
AbstractDatabaseLogger::class => Strategy\AbstractDatabaseLoggerStrategy::class
];
/**
* The method that is called when the stream is open
*
* This method will receive all parameters send to opening stream function,
* e.g. fopen($path, $mode). If the mode includes reading modes (r, r+, w+, a+, x+)
* it will trigger an error and will return false. If the searched logger
* (the 'path' file of the $path var) type is not registered in $loggersMap
* this method will trigger an error and will return false.
* Otherwise will instanciate an object and will check for registered
* strategy that will pre-prepare the object before its use for writing.
*
* @param string $path
* @param string $mode
* @param int $options
* @param string $opened_path
* @return boolean
*/
public function stream_open($path, $mode, $options, &$opened_path) {
if ($path != 'w' && $path != 'a' && $path != 'x' && ($options & STREAM_REPORT_ERRORS)) {
trigger_error("This wrapper is write-only");
return false;
}
return $this->openStream(parse_url($path), $options);
}
/**
* The actual logger preparing
*
* This method checks whether the logger is instance of Psr\Log\LoggerInterface
* and invokes its pre-prepare strategy if there is any registered
*
* @param array $url
* @param int $options
* @return boolean
*/
protected function openStream(array $url, $options) {
if (!($this->logger instanceof LoggerInterface) && ($options & STREAM_REPORT_ERRORS)) {
trigger_error("Invalid logger type");
return false;
}
if (empty($url['path'])) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error("Invalid log level");
}
return false;
}
$this->level = substr($url['path'], 1);
try {
$this->prepareLoggerStrategy($url);
}
catch (InvalidArgumentException $e) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error($e->getMessage());
}
return false;
}
return true;
}
/**
* Instantiate and invoke the logger pre-prepare strategy
*
* If a strategy is registered for logger's class, parent classes or
* its interfaces, it will be instantiated and its prepareLogger()
* method will be invoked.
* If no strategy is registered for the logger's class, BUT IS registered
* for its parent classes and/or interfaces, ONLY the first one will be used.
* The parend classes are prior the interfaces.
*
* @param array $url
* @return void
*/
protected function prepareLoggerStrategy(array $url) {
if (!empty($url['query'])) {
$query = urldecode($url['query']);
parse_str($query, $url['query']);
}
else {
$url['query'] = [];
}
if (empty($this->strategy)) {
$this->loadStrategy();
}
if ($this->strategy instanceof Strategy\StreamLoggerStrategyInterface) {
$this->strategy->prepareLogger($this->logger, $url);
}
}
/**
* Load strategy object based on $loggerStrategies
*
* @return null|void Null if there is no strategy. Otherwise is void
*/
protected function loadStrategy() {
$class = get_class($this->logger);
if (array_key_exists($class, $this->loggerStrategies)) {
$strategyClass = $this->loggerStrategies[$class];
}
else {
$parents = class_parents($class);
$interfaces = class_implements($class);
$searched = array_merge($parents, $interfaces);
$intersects = array_intersect(array_keys($this->loggerStrategies), $searched);
if ($intersects) {
$strategyClass = $this->loggerStrategies[array_shift($intersects)];
}
else {
return null;
}
}
$this->strategy = new $strategyClass();
}
/**
* Writes the log
*
* Do the actual logging, it call the log() method of the logger. The
* data can be string with the message, or serialized array with first
* element - the message, and second element - the context of the log.
* If there are more elements, they will be ignored
*
* @param string $data The log message - string or serialized array with message and context
* @return int The $data string length
*/
public function stream_write($data) {
$regex = '/^a\:\d+\:\{(.*)(\;\}\})$/i';
if (preg_match($regex, $data)) {
$message = unserialize($data);
if (count($message) == 1) {
$message[1] = [];
}
else if (!is_array($message[1])) {
$message[1] = [$message[1]];
}
}
else {
$message = [$data, []];
}
$this->logger->log($this->level, $message[0], $message[1]);
return strlen($data);
}
}
|