PHP Classes
elePHPant
Icontem

File: docs/files/Stream/StreamLoggerTrait.php.txt

Recommend this page to a friend!
  Classes of Kiril Savchev  >  ITE Logger  >  docs/files/Stream/StreamLoggerTrait.php.txt  >  Download  
File: docs/files/Stream/StreamLoggerTrait.php.txt
Role: Documentation
Content type: text/plain
Description: Documentation
Class: ITE Logger
Log messages to different storage PSR-3 compliant
Author: By
Last change: Change stream logger and strategy loading and leave their init to the concrete stream class
Date: 3 years ago
Size: 7,487 bytes
 

 

Contents

Class file image Download
<?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);
        }

}