Completed
Push — 2.1 ( 497a07...c421a1 )
by
unknown
11:23
created

Profiler::setTargets()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\profile;
9
10
use Yii;
11
use yii\base\Component;
12
use yii\base\InvalidArgumentException;
13
14
/**
15
 * Profiler provides profiling support. It stores profiling messages in the memory and sends them to different targets
16
 * according to [[targets]].
17
 *
18
 * A Profiler instance can be accessed via `Yii::getProfiler()`.
19
 *
20
 * For convenience, a set of shortcut methods are provided for profiling via the [[Yii]] class:
21
 *
22
 * - [[Yii::beginProfile()]]
23
 * - [[Yii::endProfile()]]
24
 *
25
 * For more details and usage information on Profiler, see the [guide article on profiling](guide:runtime-profiling)
26
 *
27
 * @author Paul Klimov <[email protected]>
28
 * @since 2.1
29
 */
30
class Profiler extends Component implements ProfilerInterface
31
{
32
    /**
33
     * @var bool whether to profiler is enabled. Defaults to true.
34
     * You may use this field to disable writing of the profiling messages and thus save the memory usage.
35
     */
36
    public $enabled = true;
37
    /**
38
     * @var array[] complete profiling messages.
39
     * Each message has a following keys:
40
     *
41
     * - token: string, profiling token.
42
     * - category: string, message category.
43
     * - beginTime: float, profiling begin timestamp obtained by microtime(true).
44
     * - endTime: float, profiling end timestamp obtained by microtime(true).
45
     * - duration: float, profiling block duration in milliseconds.
46
     * - beginMemory: int, memory usage at the beginning of profile block in bytes, obtained by `memory_get_usage()`.
47
     * - endMemory: int, memory usage at the end of profile block in bytes, obtained by `memory_get_usage()`.
48
     * - memoryDiff: int, a diff between 'endMemory' and 'beginMemory'.
49
     */
50
    public $messages = [];
51
52
    /**
53
     * @var array pending profiling messages, e.g. the ones which have begun but not ended yet.
54
     */
55
    private $_pendingMessages = [];
56
    /**
57
     * @var array|Target[] the profiling targets. Each array element represents a single [[Target|profiling target]] instance
58
     * or the configuration for creating the profiling target instance.
59
     * @since 2.1
60
     */
61
    private $_targets = [];
62
    /**
63
     * @var bool whether [[targets]] have been initialized, e.g. ensured to be objects.
64
     * @since 2.1
65
     */
66
    private $_isTargetsInitialized = false;
67
68
69
    /**
70
     * Initializes the profiler by registering [[flush()]] as a shutdown function.
71
     */
72 4
    public function init()
73
    {
74 4
        parent::init();
75 4
        register_shutdown_function([$this, 'flush']);
76 4
    }
77
78
    /**
79
     * @return Target[] the profiling targets. Each array element represents a single [[Target|profiling target]] instance.
80
     */
81 2
    public function getTargets()
82
    {
83 2
        if (!$this->_isTargetsInitialized) {
84 2
            foreach ($this->_targets as $name => $target) {
85 2
                if (!$target instanceof Target) {
86 2
                    $this->_targets[$name] = Yii::createObject($target);
87
                }
88
            }
89 2
            $this->_isTargetsInitialized = true;
90
        }
91 2
        return $this->_targets;
92
    }
93
94
    /**
95
     * @param array|Target[] $targets the profiling targets. Each array element represents a single [[Target|profiling target]] instance
96
     * or the configuration for creating the profiling target instance.
97
     */
98 1
    public function setTargets($targets)
99
    {
100 1
        $this->_targets = $targets;
101 1
        $this->_isTargetsInitialized = false;
102 1
    }
103
104
    /**
105
     * Adds extra target to [[targets]].
106
     * @param Target|array $target the log target instance or its DI compatible configuration.
107
     * @param string|null $name array key to be used to store target, if `null` is given target will be append
108
     * to the end of the array by natural integer key.
109
     */
110 2
    public function addTarget($target, $name = null)
111
    {
112 2
        if (!$target instanceof Target) {
113
            $this->_isTargetsInitialized = false;
114
        }
115 2
        if ($name === null) {
116 2
            $this->_targets[] = $target;
117
        } else {
118 1
            $this->_targets[$name] = $target;
119
        }
120 2
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 1336
    public function begin($token, array $context = [])
126
    {
127 1336
        if (!$this->enabled) {
128 1
            return;
129
        }
130
131 1336
        $category = isset($context['category']) ?: 'application';
132
133 1336
        $message = array_merge($context, [
134 1336
            'token' => $token,
135 1336
            'category' => $category,
136 1336
            'beginTime' => microtime(true),
137 1336
            'beginMemory' => memory_get_usage(),
138
        ]);
139
140 1336
        $this->_pendingMessages[$category][$token][] = $message;
141 1336
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 1336
    public function end($token, array $context = [])
147
    {
148 1336
        if (!$this->enabled) {
149 1
            return;
150
        }
151
152 1336
        $category = isset($context['category']) ?: 'application';
153
154 1336
        if (empty($this->_pendingMessages[$category][$token])) {
155
            throw new InvalidArgumentException('Unexpected ' . get_called_class() . '::end() call for category "' . $category . '" token "' . $token . '". A matching begin() is not found.');
156
        }
157
158 1336
        $message = array_pop($this->_pendingMessages[$category][$token]);
159 1336
        if (empty($this->_pendingMessages[$category][$token])) {
160 1336
            unset($this->_pendingMessages[$category][$token]);
161 1336
            if (empty($this->_pendingMessages[$category])) {
162 1336
                unset($this->_pendingMessages[$category]);
163
            }
164
        }
165
166 1336
        $message = array_merge(
167 1336
            $message,
168 1336
            $context,
169
            [
170 1336
                'endTime' => microtime(true),
171 1336
                'endMemory' => memory_get_usage(),
172
            ]
173
        );
174
175 1336
        $message['duration'] = $message['endTime'] - $message['beginTime'];
176 1336
        $message['memoryDiff'] = $message['endMemory'] - $message['beginMemory'];
177
178 1336
        $this->messages[] = $message;
179 1336
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 2
    public function flush()
185
    {
186 2
        foreach ($this->_pendingMessages as $category => $categoryMessages) {
187
            foreach ($categoryMessages as $token => $messages) {
188
                if (!empty($messages)) {
189
                    Yii::warning('Unclosed profiling entry detected: category "' . $category . '" token "' . $token . '"', __METHOD__);
190
                }
191
            }
192
        }
193 2
        $this->_pendingMessages = [];
194
195 2
        if (empty($this->messages)) {
196
            return;
197
        }
198
199 2
        $messages = $this->messages;
200
        // new messages could appear while the existing ones are being handled by targets
201 2
        $this->messages = [];
202
203 2
        $this->dispatch($messages);
204 2
    }
205
206
    /**
207
     * Dispatches the profiling messages to [[targets]].
208
     * @param array $messages the profiling messages.
209
     */
210 1
    protected function dispatch($messages)
211
    {
212 1
        foreach ($this->getTargets() as $target) {
213 1
            $target->collect($messages);
214
        }
215
    }
216
}