PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/www/shop/engine/Shopware/Plugins/Default/Core/Benchmark/Bootstrap.php

https://bitbucket.org/weberlars/sot-shopware
PHP | 403 lines | 222 code | 43 blank | 138 comment | 19 complexity | 110d587853664a68fd3bdd5a2e9b362e MD5 | raw file
Possible License(s): AGPL-3.0, MIT, BSD-3-Clause, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * Shopware 4.0
  4. * Copyright Š 2012 shopware AG
  5. *
  6. * According to our dual licensing model, this program can be used either
  7. * under the terms of the GNU Affero General Public License, version 3,
  8. * or under a proprietary license.
  9. *
  10. * The texts of the GNU Affero General Public License with an additional
  11. * permission and of our proprietary license can be found at and
  12. * in the LICENSE file you have received along with this program.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * "Shopware" is a registered trademark of shopware AG.
  20. * The licensing of the program under the AGPLv3 does not imply a
  21. * trademark license. Therefore any rights, title and interest in
  22. * our trademarks remain entirely with us.
  23. *
  24. * @category Shopware
  25. * @package Shopware_Plugins
  26. * @subpackage Benchmark
  27. * @copyright Copyright (c) 2012, shopware AG (http://www.shopware.de)
  28. * @version $Id$
  29. * @author $Author$
  30. */
  31. /**
  32. * Enlight benchmark extension to benchmark database queries, controller actions and template rendering.
  33. *
  34. * The Enlight_Extensions_Benchmark_Bootstrap allows the timekeeping, memory measurement.
  35. * It writes the benchmark data into the log. Support the benchmarking of database request,
  36. * template rendering and controller events.
  37. *
  38. * todo@all: Documentation
  39. */
  40. class Shopware_Plugins_Core_Benchmark_Bootstrap extends Shopware_Components_Plugin_Bootstrap
  41. {
  42. /**
  43. * @var Enlight_Components_Log Contains an instance of the Enlight_Components_Log
  44. */
  45. protected $log;
  46. /**
  47. * @var Doctrine\DBAL\Logging\DebugStack(
  48. */
  49. protected $modelLogger;
  50. /**
  51. * @var array Contains all measured events.
  52. */
  53. protected $results = array();
  54. /**
  55. * @var null Contains the start time of the Benchmarking
  56. */
  57. protected $startTime;
  58. /**
  59. * @var null Contains the start memory size of the Benchmarking
  60. */
  61. protected $startMemory;
  62. /**
  63. * Install benchmark plugin.
  64. * Subscribes the Enlight_Controller_Front_StartDispatch event to start the benchmarking and
  65. * the Enlight_Controller_Front_DispatchLoopShutdown to stop the benchmarking.
  66. *
  67. */
  68. public function install()
  69. {
  70. $this->subscribeEvent(
  71. 'Enlight_Controller_Front_StartDispatch',
  72. 'onStartDispatch',
  73. 100
  74. );
  75. $this->subscribeEvent(
  76. 'Enlight_Controller_Front_DispatchLoopShutdown',
  77. 'onDispatchLoopShutdown',
  78. 100
  79. );
  80. return true;
  81. }
  82. /**
  83. * Returns the instance of the enlight configuration. If no configuration is set,
  84. * the logDb, logTemplate and the logController flags will be set to true.
  85. *
  86. * @return Enlight_Config
  87. */
  88. public function Config()
  89. {
  90. $config = parent::Config();
  91. if(count($config) === 0) {
  92. $config->merge(new Enlight_Config(array(
  93. 'logDb' => true,
  94. 'logTemplate' => true,
  95. 'logController' => true,
  96. 'logModel' => true,
  97. )));
  98. }
  99. return $config;
  100. }
  101. /**
  102. * Listener method of the Enlight_Controller_Front_StartDispatch event.
  103. * Set the instance of the application log resource into the internal property.
  104. * Enables the database profiler if the logDb flag in the configuration is set to true.
  105. * Activate the template debugging if the logTemplate flag in the configuration is set to true.
  106. * Register the listeners to log the controllers if the logController flag in the configuration is set to true.
  107. *
  108. * On Dispatch start activate db profiling
  109. *
  110. * @param Enlight_Event_EventArgs $args
  111. * @return void
  112. */
  113. public function onStartDispatch(Enlight_Event_EventArgs $args)
  114. {
  115. if (!Shopware()->Bootstrap()->hasResource('Log')) {
  116. return;
  117. }
  118. $this->log = Shopware()->Log();
  119. if (!empty($this->Config()->logDb)) {
  120. Shopware()->Db()->getProfiler()->setEnabled(true);
  121. }
  122. if (!empty($this->Config()->logTemplate)) {
  123. Shopware()->Template()->setDebugging(true);
  124. Shopware()->Template()->debug_tpl = 'string:';
  125. }
  126. if (!empty($this->Config()->logController)) {
  127. Shopware()->Events()->registerSubscriber($this->getListeners());
  128. }
  129. if (!empty($this->Config()->logModel)) {
  130. Shopware()->ModelConfig();
  131. $this->modelLogger = new \Doctrine\DBAL\Logging\DebugStack();
  132. Shopware()->ModelConfig()->setSQLLogger($this->modelLogger);
  133. }
  134. }
  135. /**
  136. * Listener method of the Enlight_Controller_Front_DispatchLoopShutdown event.
  137. * On Dispatch Shutdown collect sql performance results, template results and controller results
  138. * and dump to log component.
  139. *
  140. * @param Enlight_Event_EventArgs $args
  141. */
  142. public function onDispatchLoopShutdown(Enlight_Event_EventArgs $args)
  143. {
  144. if ($this->log === null) {
  145. return;
  146. }
  147. if (!empty($this->Config()->logDb)) {
  148. $this->logDb();
  149. }
  150. if (!empty($this->Config()->logTemplate)) {
  151. $this->logTemplate();
  152. }
  153. if (!empty($this->Config()->logController)) {
  154. $this->logController();
  155. }
  156. if (!empty($this->Config()->logModel)) {
  157. $this->logModel();
  158. }
  159. }
  160. /**
  161. * Logs all database process to the internal log object.
  162. * Iterates all queries of the query profiler and writes the query,
  163. * the parameter and the elapsed seconds for the query into a new row of the log.
  164. *
  165. * @return void
  166. */
  167. public function logDb()
  168. {
  169. /** @var $profiler Zend_Db_Profiler */
  170. $profiler = Shopware()->Db()->getProfiler();
  171. $rows = array(array('time', 'count', 'sql', 'params'));
  172. $counts = array(10000);
  173. $total_time = 0;
  174. $queryProfiles = $profiler->getQueryProfiles();
  175. if (!$queryProfiles) {
  176. return;
  177. }
  178. /** @var $query Zend_Db_Profiler_Query */
  179. foreach ($queryProfiles as $query) {
  180. $id = md5($query->getQuery());
  181. $total_time += $query->getElapsedSecs();
  182. if (!isset($rows[$id])) {
  183. $rows[$id] = array(
  184. number_format($query->getElapsedSecs(), 5, '.', ''),
  185. 1,
  186. $query->getQuery(),
  187. $query->getQueryParams()
  188. );
  189. $counts[$id] = $query->getElapsedSecs();
  190. } else {
  191. $rows[$id][1]++;
  192. $counts[$id] += $query->getElapsedSecs();
  193. $rows[$id][0] = number_format($counts[$id], 5, '.', '');
  194. }
  195. }
  196. array_multisort($counts, SORT_NUMERIC, SORT_DESC, $rows);
  197. $rows = array_values($rows);
  198. $total_time = round($total_time, 5);
  199. $total_count = $profiler->getTotalNumQueries();
  200. $label = "Database Querys ($total_count @ $total_time sec)";
  201. $table = array($label, $rows);
  202. $this->log->table($table);
  203. }
  204. /**
  205. * Logs all database process to the internal log object.
  206. * Iterates all queries of the query profiler and writes the query,
  207. * the parameter and the elapsed seconds for the query into a new row of the log.
  208. *
  209. * @return void
  210. */
  211. public function logModel()
  212. {
  213. $rows = array(array('time', 'count', 'sql', 'params'));
  214. $counts = array(10000);
  215. $totalTime = 0;
  216. $queries = $this->modelLogger->queries;
  217. if (empty($queries)) {
  218. return;
  219. }
  220. /** @var $query Zend_Db_Profiler_Query */
  221. foreach ($queries as $query) {
  222. $id = md5($query['sql']);
  223. $totalTime += $query['executionMS'];
  224. if (!isset($rows[$id])) {
  225. $rows[$id] = array(
  226. number_format($query['executionMS'], 5, '.', ''),
  227. 1,
  228. $query['sql'],
  229. $query['params']
  230. );
  231. $counts[$id] = $query['executionMS'];
  232. } else {
  233. $rows[$id][1]++;
  234. $counts[$id] += $query['executionMS'];;
  235. $rows[$id][0] = number_format($counts[$id], 5, '.', '');
  236. }
  237. }
  238. //array_multisort($counts, SORT_NUMERIC, SORT_DESC, $rows);
  239. $rows = array_values($rows);
  240. $totalTime = round($totalTime, 5);
  241. $totalCount = count($queries);
  242. $label = "Model Querys ($totalCount @ $totalTime sec)";
  243. $table = array($label, $rows);
  244. $this->log->table($table);
  245. }
  246. /**
  247. * Logs all rendered templates into the internal log object.
  248. * Each logged template contains the template name, the required compile time,
  249. * the required render time and the required cache time.
  250. *
  251. * @return void
  252. */
  253. public function logTemplate()
  254. {
  255. $rows = array(array('name', 'compile_time', 'render_time', 'cache_time'));
  256. $total_time = 0;
  257. foreach (Smarty_Internal_Debug::$template_data as $template_file) {
  258. $total_time += $template_file['render_time'];
  259. $total_time += $template_file['cache_time'];
  260. $template_file['name'] = str_replace($this->Application()->CorePath(), '', $template_file['name']);
  261. $template_file['name'] = str_replace($this->Application()->AppPath(), '', $template_file['name']);
  262. $template_file['compile_time'] = $this->formatTime($template_file['compile_time']);
  263. $template_file['render_time'] = $this->formatTime($template_file['render_time']);
  264. $template_file['cache_time'] = $this->formatTime($template_file['cache_time']);
  265. unset($template_file['start_time']);
  266. $rows[] = array_values($template_file);
  267. }
  268. $total_time = round($total_time, 5);
  269. $total_count = count($rows) - 1;
  270. $label = "Benchmark Template ($total_count @ $total_time sec)";
  271. $table = array($label, $rows);
  272. $this->log->table($table);
  273. }
  274. /**
  275. * Get total execution time in controller
  276. *
  277. * @return void
  278. */
  279. public function logController()
  280. {
  281. $total_time = $this->formatTime(microtime(true) - $this->startTime);
  282. $label = "Benchmark Controller ($total_time sec)";
  283. $table = array($label, $this->results);
  284. $this->log->table($table);
  285. }
  286. /**
  287. * Logs all controller events into the internal log object.
  288. * Each logged events contains the event name, the execution time and the allocated peak of memory.
  289. *
  290. * @param Enlight_Event_EventArgs $args
  291. * @return void
  292. */
  293. public function onBenchmarkEvent(Enlight_Event_EventArgs $args)
  294. {
  295. if (empty($this->results)) {
  296. $this->results[] = array('name', 'memory', 'time');
  297. $this->startTime = microtime(true);
  298. $this->startMemory = memory_get_peak_usage(true);
  299. }
  300. $this->results[] = array(
  301. 0 => str_replace('Enlight_Controller_', '', $args->getName()),
  302. 1 => $this->formatMemory(memory_get_peak_usage(true) - $this->startMemory),
  303. 2 => $this->formatTime(microtime(true) - $this->startTime)
  304. );
  305. }
  306. /**
  307. * Monitor execution time and memory on specified event points in application
  308. *
  309. * @return Enlight_Event_Subscriber_Array
  310. */
  311. public function getListeners()
  312. {
  313. $events = array(
  314. 'Enlight_Controller_Front_RouteStartup',
  315. 'Enlight_Controller_Front_RouteShutdown',
  316. 'Enlight_Controller_Front_DispatchLoopStartup',
  317. 'Enlight_Controller_Front_PreDispatch',
  318. 'Enlight_Controller_Front_PostDispatch',
  319. 'Enlight_Controller_Front_DispatchLoopShutdown',
  320. 'Enlight_Controller_Action_Init',
  321. 'Enlight_Controller_Action_PreDispatch',
  322. 'Enlight_Controller_Action_PostDispatch',
  323. 'Enlight_Plugins_ViewRenderer_PreRender',
  324. 'Enlight_Plugins_ViewRenderer_PostRender'
  325. );
  326. $listeners = new Enlight_Event_Subscriber_Array();
  327. foreach ($events as $event) {
  328. $listeners->registerListener(
  329. new Enlight_Event_Handler_Default(
  330. $event, array($this, 'onBenchmarkEvent'), -99
  331. )
  332. );
  333. }
  334. return $listeners;
  335. }
  336. /**
  337. * Format memory in a proper way
  338. *
  339. * @param $size
  340. * @return string
  341. */
  342. public static function formatMemory($size)
  343. {
  344. if (empty($size)) {
  345. return '0.00 b';
  346. }
  347. $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
  348. return @number_format($size / pow(1024, ($i = floor(log($size, 1024)))), 2, '.', '') . ' ' . $unit[$i];
  349. }
  350. /**
  351. * Format time for human readable
  352. *
  353. * @param $time
  354. * @return string
  355. */
  356. public static function formatTime($time)
  357. {
  358. return number_format($time, 5, '.', '');
  359. }
  360. }