/** * Allows the display and benchmarking of queries as they are being run * * @param string $sql Query to run, and single parameter to callback * @param callable $callback Callback to execute code * @return mixed Result of query */ protected function benchmarkQuery($sql, $callback, $parameters = array()) { $starttime = microtime(true); $startmemory = memory_get_usage(true); if (is_array($sql)) { $parameters = $sql[1]; $sql = $sql[0]; } if ($this->showQueries && Director::isDev()) { $starttime = microtime(true); $result = $callback($sql); $endtime = round(microtime(true) - $starttime, 4); $formattedSql = JdornSqlFormatter::format($sql); $rows = $result->numRecords(); echo '<pre>The following query took <b>' . $endtime . '</b>s an returned <b>' . $rows . "</b> row(s) \n"; echo 'Triggered by: <i>' . $this->findSource() . '</i></pre>'; echo $formattedSql; $results = iterator_to_array($result); if ($rows > 0) { if ($rows == 1) { dump($results[0]); } else { $linearValues = count($results[0]); if ($linearValues) { dump(implode(',', array_map(function ($item) { return $item[key($item)]; }, $results))); } else { if ($rows < 20) { dump($results); } else { dump("Too many results to display"); } } } } echo '<hr/>'; $handle = $result; $handle->rewind(); // Rewind the results } else { /* @var $handle PDOQuery */ $handle = $callback($sql); } $endtime = microtime(true); $endmemory = memory_get_usage(true); $rawsql = $sql; $select = null; // Prepared query are not so readable if (!empty($parameters)) { foreach ($parameters as $param) { $pos = strpos($sql, '?'); if ($pos !== false) { $param = '"' . $param . '"'; $sql = substr_replace($sql, $param, $pos, 1); } } } // Sometimes, ugly spaces are there $sql = preg_replace('/[[:blank:]]+/', ' ', trim($sql)); $shortsql = $sql; // Sometimes, the select statement can be very long and unreadable $matches = null; preg_match_all('/SELECT(.+?) FROM/is', $sql, $matches); $select = empty($matches[1]) ? null : trim($matches[1][0]); if (strlen($select) > 100) { $shortsql = str_replace($select, '"ClickToShowFields"', $sql); } else { $select = null; } $this->queries[] = ['raw_query' => $rawsql, 'short_query' => $shortsql, 'select' => $select, 'query' => $sql, 'start_time' => $starttime, 'end_time' => $endtime, 'duration' => $endtime - $starttime, 'memory' => $endmemory - $startmemory, 'rows' => $handle ? $handle->numRecords() : null, 'success' => $handle ? true : false, 'database' => $this->getSelectedDatabase(), 'source' => $this->findSource ? $this->findSource() : null]; return $handle; }
/** * Stuff that only needs to be done once. Builds regular expressions and sorts the reserved words. */ protected static function init() { if (self::$init) { return; } // Sort reserved word list from longest word to shortest, 3x faster than usort $reservedMap = array_combine(self::$reserved, array_map('strlen', self::$reserved)); arsort($reservedMap); self::$reserved = array_keys($reservedMap); // Set up regular expressions self::$regex_boundaries = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$boundaries)) . ')'; self::$regex_reserved = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved)) . ')'; self::$regex_reserved_toplevel = str_replace(' ', '\\s+', '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved_toplevel)) . ')'); self::$regex_reserved_newline = str_replace(' ', '\\s+', '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved_newline)) . ')'); self::$regex_function = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$functions)) . ')'; self::$init = true; }
/** * Helpful debugging helper. Pass as many arguments as you need. * Keep the call on one line to be able to output arguments names * Without arguments, it will display all object instances in the backtrace * * @return void */ function d() { // Clean buffer that may be in the way if (ob_get_contents()) { ob_end_clean(); } $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); // Where is d called is the first element of the backtrace $line = $bt[0]['line']; $file = $bt[0]['file']; // Caller $caller_function = isset($bt[1]['function']) ? $bt[1]['function'] : null; $caller_class = isset($bt[1]['class']) ? $bt[1]['class'] : null; $caller = $caller_function; if ($caller_class) { $caller = $caller_class . '::' . $caller_function; } // Probably best to avoid using this in live websites... if (Director::isLive()) { SS_Log::log("Please remove call to d() in {$file}:{$line}", SS_Log::WARN); return; } // Arguments passed to the function are stored in matches $src = file($file); $src_line = $src[$line - 1]; preg_match("/d\\((.+)\\)/", $src_line, $matches); // Find all arguments, ignore variables within parenthesis $arguments_name = []; if (!empty($matches[1])) { $arguments_name = array_map('trim', preg_split("/(?![^(]*\\)),/", $matches[1])); } $isAjax = Director::is_ajax(); // Display data nicely according to context $print = function () use($isAjax) { $args = func_get_args(); if (!$isAjax) { echo '<pre>'; } foreach ($args as $arg) { if (!$arg) { continue; } if (is_string($arg)) { echo $arg; } else { print_r($arg); } echo "\n"; } if (!$isAjax) { echo '</pre>'; } }; // Display caller info $print("{$file}:{$line} ({$caller})"); // Display data in a friendly manner $args = func_get_args(); if (empty($args)) { $arguments_name = []; foreach ($bt as $trace) { if (!empty($trace['object'])) { $line = isset($trace['line']) ? $trace['line'] : 0; $function = isset($trace['function']) ? $trace['function'] : 'unknown function'; $arguments_name[] = $function . ':' . $line; $args[] = $trace['object']; } } } $i = 0; foreach ($args as $arg) { // Echo name of the variable $len = 20; $varname = isset($arguments_name[$i]) ? $arguments_name[$i] : null; if ($varname) { $print('Value for: ' . $varname); $len = strlen($varname); } // For ajax requests, a good old print_r is much better if ($isAjax || !function_exists('dump')) { $print($arg); // Make a nice line between variables for readability if (count($args) > 1) { $print(str_repeat('-', $len)); } } else { if ($varname && is_string($arg) && strpos($varname, 'sql') !== false) { echo JdornSqlFormatter::format($arg); } else { dump($arg); } } $i++; } exit; }