Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
199 lars 1
<?php namespace Clockwork\Support\Symfony;
2
 
3
use Clockwork\Helpers\Serializer;
4
use Clockwork\Request\Log;
5
use Clockwork\Request\Request;
6
use Clockwork\Request\Timeline\Timeline;
7
 
8
use Symfony\Component\HttpKernel\Profiler\Profile;
9
 
10
class ProfileTransformer
11
{
12
	public function transform(Profile $profile)
13
	{
14
		$request = new Request([ 'id' => $profile->getToken() ]);
15
 
16
		$this->transformCacheData($profile, $request);
17
		$this->transformDoctrineData($profile, $request);
18
		$this->transformEventsData($profile, $request);
19
		$this->transformLoggerData($profile, $request);
20
		$this->transformRequestData($profile, $request);
21
		$this->transformTimeData($profile, $request);
22
		$this->transformTwigData($profile, $request);
23
 
24
		$request->subrequests = $this->getSubrequests($profile);
25
 
26
		return $request;
27
	}
28
 
29
	// Cache collector
30
 
31
	protected function transformCacheData(Profile $profile, Request $request)
32
	{
33
		if (! $profile->hasCollector('cache')) return;
34
 
35
		$data = $profile->getCollector('cache');
36
 
37
		$request->cacheQueries = $this->getCacheQueries($data);
38
		$request->cacheReads   = $data->getTotals()['reads'];
39
		$request->cacheHits    = $data->getTotals()['hits'];
40
		$request->cacheWrites  = $data->getTotals()['writes'];
41
		$request->cacheDeletes = $data->getTotals()['deletes'];
42
	}
43
 
44
	protected function getCacheQueries($data)
45
	{
46
		return array_reduce(array_map(function ($queries, $connection) {
47
			return array_filter(array_map(function ($query) use ($connection) {
48
				$value = $query['result'];
49
 
50
				if (! is_array($value) || ! count($value)) return;
51
 
52
				return [
53
					'connection' => $connection,
54
					'time'       => $query['start'],
55
					'type'       => array_values($value)[0] ? 'hit' : 'miss',
56
					'key'        => array_keys($value)[0],
57
					'value'      => '',
58
					'duration'   => $query['end'] - $query['start']
59
				];
60
			}, $queries));
61
		}, $this->unwrap($data->getCalls()), array_keys($this->unwrap($data->getCalls()))), function ($all, $queries) {
62
			return array_merge($all, $queries);
63
		}, []);
64
	}
65
 
66
	// Doctrine collector
67
 
68
	protected function transformDoctrineData(Profile $profile, Request $request)
69
	{
70
		if (! $profile->hasCollector('db')) return;
71
 
72
		$data = $profile->getCollector('db');
73
 
74
		$request->databaseDuration = $data->getTime();
75
		$request->databaseQueries = $this->getQueries($data);
76
	}
77
 
78
	protected function getQueries($data)
79
	{
80
		return array_reduce(array_map(function ($queries, $connection) {
81
			return array_filter(array_map(function ($query) use ($connection) {
82
				return [
83
					'query'      => $this->createRunnableQuery($query['sql'], $this->unwrap($query['params'])),
84
					'duration'   => $query['executionMS'] * 1000,
85
					'connection' => $connection
86
				];
87
			}, $queries));
88
		}, $data->getQueries(), array_keys($data->getQueries())), function ($all, $queries) {
89
			return array_merge($all, $queries);
90
		}, []);
91
	}
92
 
93
	protected function createRunnableQuery($query, $bindings)
94
	{
95
		foreach ($bindings as $binding) {
96
			$binding = \Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension::escapeFunction($binding);
97
 
98
			// escape backslashes in the binding (preg_replace requires to do so)
99
			$binding = str_replace('\\', '\\\\', $binding);
100
 
101
			$query = preg_replace('/\?/', $binding, $query, 1);
102
		}
103
 
104
		// highlight keywords
105
		$keywords = [
106
			'select', 'insert', 'update', 'delete', 'where', 'from', 'limit', 'is', 'null', 'having', 'group by',
107
			'order by', 'asc', 'desc'
108
		];
109
		$regexp = '/\b' . implode('\b|\b', $keywords) . '\b/i';
110
 
111
		$query = preg_replace_callback($regexp, function ($match) { return strtoupper($match[0]); }, $query);
112
 
113
		return $query;
114
	}
115
 
116
	// Events collector
117
 
118
	protected function transformEventsData(Profile $profile, Request $request)
119
	{
120
		if (! $profile->hasCollector('events')) return;
121
 
122
		$data = $profile->getCollector('events');
123
 
124
		$request->events = $this->getEvents($data);
125
	}
126
 
127
	protected function getEvents($data)
128
	{
129
		$handledEvents = array_values(array_reduce($this->unwrap($data->getCalledListeners()), function ($events, $listener) {
130
			if (! isset($events[$listener['event']])) {
131
				$events[$listener['event']] = [ 'event' => $listener['event'], 'listeners' => [] ];
132
			}
133
 
134
			$events[$listener['event']]['listeners'][] = $listener['stub'];
135
 
136
			return $events;
137
		}, []));
138
 
139
		$orphanedEvents = array_map(function ($event) {
140
			return [ 'event' => $event ];
141
		}, $this->unwrap($data->getOrphanedEvents()));
142
 
143
		return array_merge($handledEvents, $orphanedEvents);
144
	}
145
 
146
	// Log collector
147
 
148
	protected function transformLoggerData(Profile $profile, Request $request)
149
	{
150
		if (! $profile->hasCollector('logger')) return;
151
 
152
		$data = $profile->getCollector('logger');
153
 
154
		$request->log()->merge($this->getLog($data));
155
	}
156
 
157
	protected function getLog($data)
158
	{
159
		$messages = array_map(function ($log) {
160
			$context = isset($log['context']) ? $log['context'] : [];
161
			$replacements = array_filter($context, function ($v) { return ! is_array($v) && ! is_object($v) && ! is_resource($v); });
162
 
163
			return [
164
				'message' => str_replace(
165
					array_map(function ($v) { return "{{$v}}"; }, array_keys($replacements)),
166
					array_values($replacements),
167
					$log['message']
168
				),
169
				'context' => (new Serializer)->normalize($log['context']),
170
				'level'   => strtolower($log['priorityName']),
171
				'time'    => $log['timestamp']
172
			];
173
		}, $this->unwrap($data->getLogs()));
174
 
175
		return new Log($messages);
176
	}
177
 
178
	// Request collector
179
 
180
	protected function transformRequestData(Profile $profile, Request $request)
181
	{
182
		if (! $profile->hasCollector('request')) return;
183
 
184
		$data = $profile->getCollector('request');
185
 
186
		$request->method         = $data->getMethod();
187
		$request->uri            = $data->getPathInfo();
188
		$request->controller     = $this->getController($data);
189
		$request->responseStatus = $data->getStatusCode();
190
		$request->headers        = $this->unwrap($data->getRequestHeaders());
191
		$request->getData        = $this->unwrap($data->getRequestQuery());
192
		$request->postData       = $this->unwrap($data->getRequestRequest());
193
		$request->cookies        = $this->unwrap($data->getRequestCookies());
194
		$request->sessionData    = (new Serializer)->normalizeEach($this->unwrap($data->getSessionAttributes()));
195
	}
196
 
197
	protected function getController($data)
198
	{
199
		$controller = $this->unwrap($data->getController());
200
 
201
		if (! is_array($controller)) return $controller;
202
 
203
		return isset($controller['method'])
204
			? "{$controller['class']}@{$controller['method']}"
205
			: $controller['class'];
206
	}
207
 
208
	// Time collector
209
 
210
	protected function transformTimeData(Profile $profile, Request $request)
211
	{
212
		if (! $profile->hasCollector('time')) return;
213
 
214
		$data = $profile->getCollector('time');
215
 
216
		$request->time         = $data->getStartTime() / 1000;
217
		$request->responseTime = $this->getResponseTime($data);
218
 
219
		$request->timeline()->merge($this->getTimeline($data));
220
	}
221
 
222
	protected function getResponseTime($data)
223
	{
224
		$lastEvent = $data->getEvents()['__section__'];
225
 
226
		return ($lastEvent->getOrigin() + $lastEvent->getDuration()) / 1000;
227
	}
228
 
229
	protected function getTimeline($data)
230
	{
231
		$events = array_map(function ($event, $name) {
232
			if ($name == '__section__') {
233
				$name = 'Application runtime';
234
			} elseif ($name == '__section__.child') {
235
				$name = 'Subrequest';
236
			}
237
 
238
			return [
239
				'start'       => ($event->getOrigin() + $event->getStartTime()) / 1000,
240
				'end'         => ($event->getOrigin() + $event->getEndTime()) / 1000,
241
				'duration'    => $event->getDuration(),
242
				'description' => $name,
243
				'data'        => []
244
			];
245
		}, $data->getEvents(), array_keys($data->getEvents()));
246
 
247
		$topEvent = $data->getEvents()['__section__'];
248
		array_unshift($events, [
249
			'start'       => $start = $data->getStartTime() / 1000,
250
			'end'         => $end = ($topEvent->getOrigin() + $topEvent->getStartTime()) / 1000,
251
			'duration'    => ($end - $start) * 1000,
252
			'description' => 'Symfony initialization',
253
			'data'        => []
254
		]);
255
 
256
		return new Timeline($events);
257
	}
258
 
259
	// Twig collector
260
 
261
	protected function transformTwigData(Profile $profile, Request $request)
262
	{
263
		if (! $profile->hasCollector('twig')) return;
264
 
265
		$data = $profile->getCollector('twig');
266
 
267
		$request->viewsData = $this->getViews($data);
268
	}
269
 
270
	protected function getViews($data)
271
	{
272
		return array_map(function ($template) {
273
			return [
274
				'description' => 'Rendering a view',
275
				'data' => [ 'name' => $template, 'data' => [] ]
276
			];
277
		}, array_keys($data->getTemplates()));
278
	}
279
 
280
	protected function getSubrequests($profile)
281
	{
282
		return array_map(function ($child) {
283
			return [
284
				'url'  => urlencode($child->getCollector('request')->getPathInfo()),
285
				'id'   => $child->getToken(),
286
				'path' => null
287
			];
288
		}, $profile->getChildren());
289
	}
290
 
291
	protected function unwrap($data)
292
	{
293
		if ($data instanceof \Symfony\Component\VarDumper\Cloner\Data) {
294
			return $data->getValue(true);
295
		} elseif ($data instanceof \Symfony\Component\HttpFoundation\ParameterBag) {
296
			return array_map(function ($val) { return $val->getValue(); }, $data->all());
297
		} elseif (is_array($data)) {
298
			return array_map(function ($item) { return $this->unwrap($item); }, $data);
299
		}
300
 
301
		return $data;
302
	}
303
}