Log::log()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 3
dl 0
loc 7
rs 10
1
<?php
2
3
namespace alkemann\h2l;
4
5
use alkemann\h2l\exceptions\ConfigMissing;
6
7
/**
8
 * Class Log
9
 *
10
 * Follows http://www.php-fig.org/psr/psr-3/
11
 * See https://github.com/php-fig/log
12
 *
13
 *
14
 * 0 Emergency: system is unusable
15
 * 1 Alert: action must be taken immediately
16
 * 2 Critical: critical conditions
17
 * 3 Error: error conditions
18
 * 4 Warning: warning conditions
19
 * 5 Notice: normal but significant condition
20
 * 6 Informational: informational messages
21
 * 7 Debug: debug-level messages
22
 *
23
 * @package alkemann\h2l
24
 * @method static void debug($message, array $context = []) Log debug level
25
 * @method static void info($message, array $context = []) Log info level
26
 * @method static void notice($message, array $context = []) Log notice level
27
 * @method static void warning($message, array $context = []) Log warning level
28
 * @method static void error($message, array $context = []) Log error level
29
 * @method static void critical($message, array $context = []) Log critical level
30
 * @method static void alert($message, array $context = []) Log alert level
31
 * @method static void emergency($message, array $context = []) Log emergency level
32
 */
33
class Log
34
{
35
    /**
36
     * Set handlers here, either a callable or an object that implements Psr\Log\LoggerInterface
37
     * @var array<mixed>
38
     */
39
    protected static array $handlers = [];
40
41
    /**
42
     * Add handler, it should implement Psr\Log\LoggerInterface
43
     *
44
     * @param string $name unique name for this handler
45
     * @param object|callable $handler an object that implement Psr\Log\LoggerInterface or a callable
46
     */
47
    public static function handler(string $name, $handler): void
48
    {
49
        if (is_callable($handler) === false) {
50
            if (is_object($handler)) {
51
                // TODO check if object implements interface?
52
                if (!method_exists($handler, 'log')) {
53
                    throw new \InvalidArgumentException(
54
                        "$name is not a valid handler, it must implement the Psr\Log\LoggerInterface"
55
                    );
56
                }
57
            } else {
58
                throw new \InvalidArgumentException("$name is not a valid handler");
59
            }
60
        }
61
        static::$handlers[$name] = $handler;
62
    }
63
64
    /**
65
     * Support the log levels of debug, info, notice, warning, error, critical, alert, emergency
66
     *
67
     * @param string $method
68
     * @param array<mixed> $args
69
     */
70
    public static function __callStatic(string $method, array $args = []): void
71
    {
72
        /*
73
        if (!in_array($method, ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'])) {
74
            throw new \Psr\Log\InvalidArgumentException("Undefined level")
75
        }
76
        */
77
        array_unshift($args, $method);
78
        call_user_func_array(['alkemann\h2l\Log', 'log'], array_values($args));
79
    }
80
81
    /**
82
     * Send $message with level $level to all handlers
83
     *
84
     * @param string $level
85
     * @param string $message
86
     * @param array<mixed> $context
87
     */
88
    public static function log($level, $message, array $context = []): void
89
    {
90
        foreach (static::$handlers as $handlerName => $handler) {
91
            if (is_callable($handler)) { // avoid this?
92
                $handler($level, $message, $context);
93
            } else {
94
                $handler->log($level, $message, $context);
95
            }
96
        }
97
    }
98
99
    /**
100
     * A standard output handler, INFO, DEBUG, NOTICE to `php://stdout` and rest to `php://stderr`
101
     *
102
     * You can enable it like this: `Log::handler('standard', [Log::class, 'std']);`
103
     *
104
     * @codeCoverageIgnore
105
     * @param string $level
106
     * @param string $message
107
     */
108
    private static function std(string $level, string $message): void
0 ignored issues
show
Unused Code introduced by
The method std() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
109
    {
110
        $level = strtoupper($level);
111
        $string = "{$level}: {$message}\n";
112
        $channel = in_array($level, ['INFO', 'DEBUG', 'NOTICE']) ? 'php://stdout' : 'php://stderr';
113
        file_put_contents($channel, $string);
114
    }
115
116
    /**
117
     * A default naive file handler that can be used initially, but should be replaced for prod
118
     *
119
     * @codeCoverageIgnore
120
     * @param string $level
121
     * @param string $message
122
     * @throws ConfigMissing
123
     */
124
    private static function file(string $level, string $message): void
125
    {
126
        $path = Environment::get('logs_path');
127
        if (is_null($path)) {
128
            throw new ConfigMissing("File handler requires a `logs_path` in Environment");
129
        }
130
        $file = $path . 'app.log';
131
        $fileHandler = fopen($file, 'a');
132
        if ($fileHandler === false) {
133
            throw new ConfigMissing("File handler requires a `{$file}` to be writeable");
134
        }
135
        $string = date('Y-m-d H:i:s') . " " . strtoupper($level) . " " . $message . PHP_EOL;
136
        fwrite($fileHandler, $string);
137
        fclose($fileHandler);
138
    }
139
}
140