From 6266264bcc5a75a4a8cafcedb525be942fe4f7be Mon Sep 17 00:00:00 2001 From: Big_Shark Date: Wed, 26 Aug 2015 00:15:56 +0700 Subject: [PATCH] Added propel2 collector and tests --- src/DebugBar/Bridge/Propel2Collector.php | 307 ++++++++++++++++++ .../DataCollector/Propel2CollectorTest.php | 239 ++++++++++++++ 2 files changed, 546 insertions(+) create mode 100644 src/DebugBar/Bridge/Propel2Collector.php create mode 100644 tests/DebugBar/Tests/DataCollector/Propel2CollectorTest.php diff --git a/src/DebugBar/Bridge/Propel2Collector.php b/src/DebugBar/Bridge/Propel2Collector.php new file mode 100644 index 0000000..3df4dcc --- /dev/null +++ b/src/DebugBar/Bridge/Propel2Collector.php @@ -0,0 +1,307 @@ + + * $debugbar->addCollector(new \DebugBar\Bridge\Propel2Collector(\Propel\Runtime\Propel::getServiceContainer()->getReadConnection())); + * + */ +class Propel2Collector extends DataCollector implements Renderable, AssetProvider +{ + /** + * @var null|TestHandler + */ + protected $handler = null; + + /** + * @var null|Logger + */ + protected $logger = null; + + /** + * @var array + */ + protected $config = array(); + + /** + * @var array + */ + protected $errors = array(); + + /** + * @var int + */ + protected $queryCount = 0; + + /** + * @param ConnectionInterface $connection Propel connection + */ + public function __construct( + ConnectionInterface $connection, + array $logMethods = array( + 'beginTransaction', + 'commit', + 'rollBack', + 'forceRollBack', + 'exec', + 'query', + 'execute' + ) + ) { + if ($connection instanceof ProfilerConnectionWrapper) { + $connection->setLogMethods($logMethods); + + $this->config = $connection->getProfiler()->getConfiguration(); + + $this->handler = new TestHandler(); + + if ($connection->getLogger() instanceof Logger) { + $this->logger = $connection->getLogger(); + $this->logger->pushHandler($this->handler); + } else { + $this->errors[] = 'Supported only monolog logger'; + } + } else { + $this->errors[] = 'You need set ProfilerConnectionWrapper'; + } + } + + /** + * @return TestHandler|null + */ + public function getHandler() + { + return $this->handler; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return Logger|null + */ + public function getLogger() + { + return $this->logger; + } + + /** + * @return LoggerInterface + */ + protected function getDefaultLogger() + { + return Propel::getServiceContainer()->getLogger(); + } + + /** + * @return int + */ + protected function getQueryCount() + { + return $this->queryCount; + } + + /** + * @param array $records + * @param array $config + * @return array + */ + protected function getStatements($records, $config) + { + $statements = array(); + foreach ($records as $record) { + $duration = null; + $memory = null; + + $isSuccess = ( LogLevel::INFO === strtolower($record['level_name']) ); + + $detailsCount = count($config['details']); + $parameters = explode($config['outerGlue'], $record['message'], $detailsCount + 1); + if (count($parameters) === ($detailsCount + 1)) { + $parameters = array_map('trim', $parameters); + $_details = array(); + foreach (array_splice($parameters, 0, $detailsCount) as $string) { + list($key, $value) = array_map('trim', explode($config['innerGlue'], $string, 2)); + $_details[$key] = $value; + } + + $details = array(); + foreach ($config['details'] as $key => $detail) { + if (isset($_details[$detail['name']])) { + $value = $_details[$detail['name']]; + if ('time' === $key) { + if (substr_count($value, 'ms')) { + $value = (float)$value / 1000; + } else { + $value = (float)$value; + } + } else { + $suffixes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + $suffix = substr($value, -2); + $i = array_search($suffix, $suffixes, true); + $i = (false === $i) ? 0 : $i; + + $value = ((float)$value) * pow(1024, $i); + } + $details[$key] = $value; + } + } + + if (isset($details['time'])) { + $duration = $details['time']; + } + if (isset($details['memDelta'])) { + $memory = $details['memDelta']; + } + + $message = end($parameters); + + if ($isSuccess) { + $this->queryCount++; + } + + } else { + $message = $record['message']; + } + + $statement = array( + 'sql' => $message, + 'is_success' => $isSuccess, + 'duration' => $duration, + 'duration_str' => $this->getDataFormatter()->formatDuration($duration), + 'memory' => $memory, + 'memory_str' => $this->getDataFormatter()->formatBytes($memory), + ); + + if (false === $isSuccess) { + $statement['sql'] = ''; + $statement['error_code'] = $record['level']; + $statement['error_message'] = $message; + } + + $statements[] = $statement; + } + return $statements; + } + + /** + * @return array + */ + public function collect() + { + if (count($this->errors)) { + return array( + 'statements' => array_map(function ($message) { + return array('sql' => '', 'is_success' => false, 'error_code' => 500, 'error_message' => $message); + }, $this->errors), + 'nb_statements' => 0, + 'nb_failed_statements' => count($this->errors), + ); + } + + if ($this->getHandler() === null) { + return array(); + } + + $statements = $this->getStatements($this->getHandler()->getRecords(), $this->getConfig()); + + $failedStatement = count(array_filter($statements, function ($statement) { + return false === $statement['is_success']; + })); + $accumulatedDuration = array_reduce($statements, function ($accumulatedDuration, $statement) { + + $time = isset($statement['duration']) ? $statement['duration'] : 0; + return $accumulatedDuration += $time; + }); + $memoryUsage = array_reduce($statements, function ($memoryUsage, $statement) { + + $time = isset($statement['memory']) ? $statement['memory'] : 0; + return $memoryUsage += $time; + }); + + return array( + 'nb_statements' => $this->getQueryCount(), + 'nb_failed_statements' => $failedStatement, + 'accumulated_duration' => $accumulatedDuration, + 'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($accumulatedDuration), + 'memory_usage' => $memoryUsage, + 'memory_usage_str' => $this->getDataFormatter()->formatBytes($memoryUsage), + 'statements' => $statements + ); + } + + /** + * @return string + */ + public function getName() + { + $additionalName = ''; + if ($this->getLogger() !== $this->getDefaultLogger()) { + $additionalName = ' ('.$this->getLogger()->getName().')'; + } + + return 'propel2'.$additionalName; + } + + /** + * @return array + */ + public function getWidgets() + { + return array( + $this->getName() => array( + 'icon' => 'bolt', + 'widget' => 'PhpDebugBar.Widgets.SQLQueriesWidget', + 'map' => $this->getName(), + 'default' => '[]' + ), + $this->getName().':badge' => array( + 'map' => $this->getName().'.nb_statements', + 'default' => 0 + ), + ); + } + + /** + * @return array + */ + public function getAssets() + { + return array( + 'css' => 'widgets/sqlqueries/widget.css', + 'js' => 'widgets/sqlqueries/widget.js' + ); + } +} diff --git a/tests/DebugBar/Tests/DataCollector/Propel2CollectorTest.php b/tests/DebugBar/Tests/DataCollector/Propel2CollectorTest.php new file mode 100644 index 0000000..d443eec --- /dev/null +++ b/tests/DebugBar/Tests/DataCollector/Propel2CollectorTest.php @@ -0,0 +1,239 @@ + 0.1, + 'details' => array( + 'time' => array( + 'name' => 'Time', + 'precision' => 3, + 'pad' => 8, + ), + 'mem' => array( + 'name' => 'Memory', + 'precision' => 3, + 'pad' => 8, + ), + 'memDelta' => array( + 'name' => 'Memory Delta', + 'precision' => 3, + 'pad' => 8, + ), + 'memPeak' => array( + 'name' => 'Memory Peak', + 'precision' => 3, + 'pad' => 8, + ), + ), + 'innerGlue' => ': ', + 'outerGlue' => ' | ', + ); + + $stub = $this->getMockBuilder('DebugBar\Bridge\Propel2Collector') + ->disableOriginalConstructor() + ->setMethods(array('getDataFormatter', 'getHandler', 'getConfig')) + ->getMock(); + + $this->dataFormatter = new DataFormatter(); + + $stub->method('getDataFormatter')->willReturn($this->dataFormatter); + + $stub->method('getConfig')->willReturn($config); + + $this->stub = $stub; + } + + protected function equals($correctResult, $record) + { + $this->stub->method('getHandler')->willReturn(new MockHandler($record)); + $this->assertEquals($correctResult, $this->stub->collect()); + } + + public function testSimpleMessage() + { + $record = array( + 'message' => 'Simple message', + 'context' => 'propel', + 'level' => 200, + 'level_name' => 'INFO', + 'channel' => 'propel', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ); + + $correctResult = array( + 'nb_statements' => 0, + 'nb_failed_statements' => 0, + 'accumulated_duration' => 0, + 'accumulated_duration_str' => $this->dataFormatter->formatDuration(0), + 'memory_usage' => 0, + 'memory_usage_str' => $this->dataFormatter->formatBytes(0), + 'statements' => array( + array( + 'sql' => 'Simple message', + 'is_success' => true, + 'duration' => null, + 'duration_str' => $this->dataFormatter->formatDuration(0), + 'memory' => null, + 'memory_str' => $this->dataFormatter->formatBytes(0), + ), + ) + ); + $this->equals($correctResult, array($record)); + } + + public function testErrorMessage() + { + $record = array( + 'message' => 'Error message', + 'context' => 'propel', + 'level' => 500, + 'level_name' => 'critical', + 'channel' => 'propel', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ); + + $correctResult = array( + 'nb_statements' => 0, + 'nb_failed_statements' => 1, + 'accumulated_duration' => 0, + 'accumulated_duration_str' => $this->dataFormatter->formatDuration(0), + 'memory_usage' => 0, + 'memory_usage_str' => $this->dataFormatter->formatBytes(0), + 'statements' => array( + array( + 'sql' => '', + 'is_success' => false, + 'error_code' => 500, + 'error_message' => 'Error message', + 'duration' => null, + 'duration_str' => $this->dataFormatter->formatDuration(0), + 'memory' => null, + 'memory_str' => $this->dataFormatter->formatBytes(0), + ), + ) + ); + + $this->equals($correctResult, array($record)); + } + + public function testProfileMessage() + { + $record = array( + 'message' => ' Time: 0.100ms | Memory: 1MB | Memory Delta: +1.0kB | Memory Peak: 2MB | SELECT id, first_name, last_name FROM author WHERE id = 1', + 'context' => 'propel', + 'level' => 200, + 'level_name' => 'info', + 'channel' => 'propel', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ); + + $correctResult = array( + 'nb_statements' => 1, + 'nb_failed_statements' => 0, + 'accumulated_duration' => 0.0001, + 'accumulated_duration_str' => $this->dataFormatter->formatDuration(0.0001), + 'memory_usage' => 1024.0, + 'memory_usage_str' => $this->dataFormatter->formatBytes(1024.0), + 'statements' => array( + array( + 'sql' => 'SELECT id, first_name, last_name FROM author WHERE id = 1', + 'is_success' => true, + 'duration' => 0.0001, + 'duration_str' => $this->dataFormatter->formatDuration(0.0001), + 'memory' => 1024.0, + 'memory_str' => $this->dataFormatter->formatBytes(1024.0), + ), + ) + ); + + $this->equals($correctResult, array($record)); + } + + public function testSummaryProfileMessage() + { + $records = array( + array( + 'message' => ' Time: 0.100ms | Memory: 1MB | Memory Delta: +1.0kB | Memory Peak: 2MB | SELECT id, first_name, last_name FROM author WHERE id = 1', + 'context' => 'propel', + 'level' => 200, + 'level_name' => 'info', + 'channel' => 'propel', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ), + array( + 'message' => ' Time: 0.100ms | Memory: 1MB | Memory Delta: +1.0kB | Memory Peak: 2MB | SELECT id, first_name, last_name FROM author WHERE id = 1', + 'context' => 'propel', + 'level' => 200, + 'level_name' => 'info', + 'channel' => 'propel', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ), + ); + + $correctResult = array( + 'nb_statements' => 2, + 'nb_failed_statements' => 0, + 'accumulated_duration' => 0.0002, + 'accumulated_duration_str' => $this->dataFormatter->formatDuration(0.0002), + 'memory_usage' => 2048, + 'memory_usage_str' => $this->dataFormatter->formatBytes(2048), + 'statements' => array( + array( + 'sql' => 'SELECT id, first_name, last_name FROM author WHERE id = 1', + 'is_success' => true, + 'duration' => 0.0001, + 'duration_str' => $this->dataFormatter->formatDuration(0.0001), + 'memory' => 1024.0, + 'memory_str' => $this->dataFormatter->formatBytes(1024.0), + ), + array( + 'sql' => 'SELECT id, first_name, last_name FROM author WHERE id = 1', + 'is_success' => true, + 'duration' => 0.0001, + 'duration_str' => $this->dataFormatter->formatDuration(0.0001), + 'memory' => 1024.0, + 'memory_str' => $this->dataFormatter->formatBytes(1024.0), + ), + ) + ); + $this->equals($correctResult, $records); + } +} + +class MockHandler +{ + + protected $records = array(); + public function __construct($records) + { + $this->records = $records; + } + + /** + * @return array + */ + public function getRecords() + { + return $this->records; + } +}