Passed
Pull Request — master (#19)
by Rustam
02:33
created

FileTarget   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 34
c 3
b 0
f 0
dl 0
loc 103
ccs 37
cts 37
cp 1
rs 10
wmc 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setFilename() 0 3 1
A __construct() 0 3 1
A resolveFilename() 0 18 4
A export() 0 23 3
A formatMessage() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Profiler;
6
7
use Yiisoft\Aliases\Aliases;
8
use Yiisoft\Files\FileHelper;
9
10
/**
11
 * FileTarget records profiling messages in a file specified via {@see filename}.
12
 *
13
 * Application configuration example:
14
 *
15
 * ```php
16
 * return [
17
 *     'profiler' => [
18
 *         'targets' => [
19
 *             [
20
 *                 '__class' => Yiisoft\Profile\FileTarget::class,
21
 *                 //'filename' => '@runtime/profiling/{date}-{time}.txt',
22
 *             ],
23
 *         ],
24
 *         // ...
25
 *     ],
26
 *     // ...
27
 * ];
28
 * ```
29
 */
30
final class FileTarget extends Target
31
{
32
    /**
33
     * @var string file path or [path alias](guide:concept-aliases). File name may contain the placeholders,
34
     * which will be replaced by computed values. The supported placeholders are:
35
     *
36
     * - '{ts}' - profiling completion timestamp.
37
     * - '{date}' - profiling completion date in format 'ymd'.
38
     * - '{time}' - profiling completion time in format 'His'.
39
     *
40
     * The directory containing the file will be automatically created if not existing.
41
     * If target file is already exist it will be overridden.
42
     */
43
    private string $filename = '@runtime/profiling/{date}-{time}.txt';
44
45
    /**
46
     * @var int the permission to be set for newly created directories.
47
     * This value will be used by PHP chmod() function. No umask will be applied.
48
     * Defaults to 0775, meaning the directory is read-writable by owner and group,
49
     * but read-only for other users.
50
     */
51
    private int $dirMode = 0775;
52
    private Aliases $aliases;
53
54 4
    public function __construct(Aliases $aliases)
55
    {
56 4
        $this->aliases = $aliases;
57 4
    }
58
59 3
    public function export(array $messages): void
60
    {
61 3
        $memoryPeakUsage = memory_get_peak_usage();
62
63
        // TODO: make sure it works with RoadRunner and alike servers
64 3
        $totalTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
65 3
        $text = "Total processing time: {$totalTime} ms; Peak memory: {$memoryPeakUsage} B. \n\n";
66
67 3
        $text .= implode("\n", array_map([$this, 'formatMessage'], $messages));
68
69 3
        $filename = $this->resolveFilename();
70
71 3
        if (file_exists($filename)) {
72 2
            FileHelper::unlink($filename);
73
        } else {
74 1
            $filePath = dirname($filename);
75
76 1
            if (!is_dir($filePath)) {
77 1
                FileHelper::createDirectory($filePath, $this->dirMode);
78
            }
79
        }
80
81 3
        file_put_contents($filename, $text);
82 3
    }
83
84
    /**
85
     * Set profiles filename
86
     *
87
     * @param string $value
88
     */
89 4
    public function setFilename(string $value): void
90
    {
91 4
        $this->filename = $value;
92 4
    }
93
94
    /**
95
     * Resolves value of {@see filename} processing path alias and placeholders.
96
     *
97
     * @return string actual target filename.
98
     */
99 3
    protected function resolveFilename(): string
100
    {
101 3
        $filename = $this->aliases->get($this->filename);
102
103 3
        return preg_replace_callback(
104 3
            '/{\\w+}/',
105 3
            static function ($matches) {
106 1
                switch ($matches[0]) {
107 1
                    case '{ts}':
108 1
                        return time();
109 1
                    case '{date}':
110 1
                        return gmdate('ymd');
111 1
                    case '{time}':
112 1
                        return gmdate('His');
113
                }
114 1
                return $matches[0];
115 3
            },
116
            $filename
117
        );
118
    }
119
120
    /**
121
     * Formats a profiling message for display as a string.
122
     *
123
     * @param Message $message the profiling message to be formatted.
124
     * The message structure follows that in {@see Profiler::$messages}.
125
     *
126
     * @return string the formatted message.
127
     */
128 3
    private function formatMessage(Message $message): string
129
    {
130 3
        return date('Y-m-d H:i:s', (int)$message->context('beginTime'))
131 3
            . " [{$message->context('duration')} ms][{$message->context('memoryDiff')} B][{$message->level()}] {$message->message()}"
132 3
            . __METHOD__;
133
    }
134
}
135