diff --git a/includes/pqp/README.txt b/includes/pqp/README.txt new file mode 100644 index 000000000..a01c8cf93 --- /dev/null +++ b/includes/pqp/README.txt @@ -0,0 +1,101 @@ +PHP Quick Profiler README +http://particletree.com/features/php-quick-profiler/ + +#### On This Page #### + +1. Introduction and Overview of Files +2. Getting the Example Working +3. Setting up the Database Class +4. Using Smarty instead of PHP echos + +##################################### +1. Introduction and Overview of Files +##################################### + +PHP Quick Profiler is a helper class that outputs debugging related information +to the screen when the page has finished executing. This zip package contains a +functional example project that utilizes the helper classes. + +- index.php : The landing page of the example. Navigate to it in your browser to see the demo. +- display.php : Contains the markup for PQP. +- pqp.tpl : A Smarty variation of the PQP markup. +- /css/ : The stylesheets used by PQP. +- /images/ : The images used by PQP. +- /classes/Console.php : The class used to log items to the PQP display. +- /classes/MySqlDatabase : A sample database wrapper to explain how database logging could be implemented. +- /classes/PhpQuickProfiler : The core class that compiles the data before outputting to the browser. + +############################## +2. Getting the Example Working +############################## + +For the most part, the example will work once you drop it in your root directory. +There are a few settings to check though. + +- In PHPQuickProfiler.php, set the $config member variable to the path relative to your root (located in the constructor). +- If PQP does not appear after navigating to index.php in your browser, locate the destructor +of the PQPExample class (at the bottom). Rename the function from __destruct() to display(). Then, +manually call the function display() just underneath the class after the call to init(). The reason this would +happen is because the destructor is not firing on your server configuration. +- At this point, everything should work except for the database tab. + +################################ +3. Setting up the Database Class +################################ + +NOTE - This step does require knowledge on PHP / Database interactions. There is no copy/paste solution. + +Logging database data is by far the hardest part of integrating PQP into your own project. It +requires that you have some sort of database wrapper around your code. If you do, it should be easy to implement. +To show you how it works, follow these steps with the sample database class we have provided. + +- Create a database named 'test' and run the following query on it. + +CREATE TABLE `Posts` ( + `PostId` int(11) unsigned NOT NULL default '0', + PRIMARY KEY (`PostId`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 + +- In index.php, uncomment out the second include, which includes the database class. +- In index.php, uncomment out the function sampleDatabaseData(). +- In the sampleDatabaseData(), supply your database host, username, password, and database name. + +Given those steps, database logging will be enabled. If you would like to transition this to your own database class, +open /classes/MySqlDatabase.php and note the following: + +- $queryCount and $queries member variables declared on initialization +- When a query is run, the following is executed: + +$start = $this->getTime(); +$rs = mysql_query($sql, $this->conn); +$this->queryCount += 1; +$this->logQuery($sql, $start); + +- Everything in /classes/MySqlDatabase.php under the section comment "Debugging" +must be available for the above snippet to work. + +#################################### +4. Using Smarty instead of PHP echos +#################################### + +We love Smarty and hate echos, but to make this work for everyone we set the default as echos. To show love +to the Smarty users out there, we have included a pqp.tpl file for PQP. To make it work, you would have to change +the following in /classes/PhpQuickProfiler.php: + +- Add a require_once to your Smarty Library. +- In the constructor, declare an instance of Smarty: $this->smarty = new Smarty(...); +- Everywhere in in the code you see $this->output[... change it to a smarty assign. For example: + +$this->output['logs'] = $logs; + +... becomes ... + +$this->smarty->assign('logs', $logs); + +After doing it once, you'll see the pattern and can probably use a find/replace to do the rest quickly. + +- Locate the display() function at the bottom. Remove the last 2 lines, and add: + +$this->smarty->display('pathToDisplay.tpl'); + +All set after that! diff --git a/includes/pqp/classes/Console.php b/includes/pqp/classes/Console.php new file mode 100644 index 000000000..965adc8ea --- /dev/null +++ b/includes/pqp/classes/Console.php @@ -0,0 +1,90 @@ + $data, + "type" => 'log' + ); + $GLOBALS['debugger_logs']['console'][] = $logItem; + $GLOBALS['debugger_logs']['logCount'] += 1; + } + + /*--------------------------------------------------- + LOG MEMORY USAGE OF VARIABLE OR ENTIRE SCRIPT + -----------------------------------------------------*/ + + public function logMemory($object = false, $name = 'PHP') { + $memory = memory_get_usage(); + if($object) $memory = strlen(serialize($object)); + $logItem = array( + "data" => $memory, + "type" => 'memory', + "name" => $name, + "dataType" => gettype($object) + ); + $GLOBALS['debugger_logs']['console'][] = $logItem; + $GLOBALS['debugger_logs']['memoryCount'] += 1; + } + + /*----------------------------------- + LOG A PHP EXCEPTION OBJECT + ------------------------------------*/ + + public function logError($exception, $message) { + $logItem = array( + "data" => $message, + "type" => 'error', + "file" => $exception->getFile(), + "line" => $exception->getLine() + ); + $GLOBALS['debugger_logs']['console'][] = $logItem; + $GLOBALS['debugger_logs']['errorCount'] += 1; + } + + /*------------------------------------ + POINT IN TIME SPEED SNAPSHOT + -------------------------------------*/ + + public function logSpeed($name = 'Point in Time') { + $logItem = array( + "data" => PhpQuickProfiler::getMicroTime(), + "type" => 'speed', + "name" => $name + ); + $GLOBALS['debugger_logs']['console'][] = $logItem; + $GLOBALS['debugger_logs']['speedCount'] += 1; + } + + /*----------------------------------- + SET DEFAULTS & RETURN LOGS + ------------------------------------*/ + + public function getLogs() { + if(!$GLOBALS['debugger_logs']['memoryCount']) $GLOBALS['debugger_logs']['memoryCount'] = 0; + if(!$GLOBALS['debugger_logs']['logCount']) $GLOBALS['debugger_logs']['logCount'] = 0; + if(!$GLOBALS['debugger_logs']['speedCount']) $GLOBALS['debugger_logs']['speedCount'] = 0; + if(!$GLOBALS['debugger_logs']['errorCount']) $GLOBALS['debugger_logs']['errorCount'] = 0; + return $GLOBALS['debugger_logs']; + } +} + +?> \ No newline at end of file diff --git a/includes/pqp/classes/MySqlDatabase.php b/includes/pqp/classes/MySqlDatabase.php new file mode 100644 index 000000000..51225a7c3 --- /dev/null +++ b/includes/pqp/classes/MySqlDatabase.php @@ -0,0 +1,115 @@ +host = $host; + $this->user = $user; + $this->password = $password; + } + + function connect($new = false) { + $this->conn = mysql_connect($this->host, $this->user, $this->password, $new); + if(!$this->conn) { + throw new Exception('We\'re working on a few connection issues.'); + } + } + + function changeDatabase($database) { + $this->database = $database; + if($this->conn) { + if(!mysql_select_db($database, $this->conn)) { + throw new CustomException('We\'re working on a few connection issues.'); + } + } + } + + function lazyLoadConnection() { + $this->connect(true); + if($this->database) $this->changeDatabase($this->database); + } + + /*----------------------------------- + QUERY + ------------------------------------*/ + + function query($sql) { + if(!$this->conn) $this->lazyLoadConnection(); + $start = $this->getTime(); + $rs = mysql_query($sql, $this->conn); + $this->queryCount += 1; + $this->logQuery($sql, $start); + if(!$rs) { + throw new Exception('Could not execute query.'); + } + return $rs; + } + + /*----------------------------------- + DEBUGGING + ------------------------------------*/ + + function logQuery($sql, $start) { + $query = array( + 'sql' => $sql, + 'time' => ($this->getTime() - $start)*1000 + ); + array_push($this->queries, $query); + } + + function getTime() { + $time = microtime(); + $time = explode(' ', $time); + $time = $time[1] + $time[0]; + $start = $time; + return $start; + } + + public function getReadableTime($time) { + $ret = $time; + $formatter = 0; + $formats = array('ms', 's', 'm'); + if($time >= 1000 && $time < 60000) { + $formatter = 1; + $ret = ($time / 1000); + } + if($time >= 60000) { + $formatter = 2; + $ret = ($time / 1000) / 60; + } + $ret = number_format($ret,3,'.','') . ' ' . $formats[$formatter]; + return $ret; + } + + function __destruct() { + @mysql_close($this->conn); + } + +} + +?> diff --git a/includes/pqp/classes/PhpQuickProfiler.php b/includes/pqp/classes/PhpQuickProfiler.php new file mode 100644 index 000000000..9b77d5b71 --- /dev/null +++ b/includes/pqp/classes/PhpQuickProfiler.php @@ -0,0 +1,204 @@ +startTime = $startTime; + $this->config = $config; + require_once($config.'classes/Console.php'); + } + + /*------------------------------------------- + FORMAT THE DIFFERENT TYPES OF LOGS + -------------------------------------------*/ + + public function gatherConsoleData() { + $logs = Console::getLogs(); + if($logs['console']) { + foreach($logs['console'] as $key => $log) { + if($log['type'] == 'log') { + $logs['console'][$key]['data'] = print_r($log['data'], true); + } + elseif($log['type'] == 'memory') { + $logs['console'][$key]['data'] = $this->getReadableFileSize($log['data']); + } + elseif($log['type'] == 'speed') { + $logs['console'][$key]['data'] = $this->getReadableTime(($log['data'] - $this->startTime)*1000); + } + } + } + $this->output['logs'] = $logs; + } + + /*------------------------------------------- + AGGREGATE DATA ON THE FILES INCLUDED + -------------------------------------------*/ + + public function gatherFileData() { + $files = get_included_files(); + $fileList = array(); + $fileTotals = array( + "count" => count($files), + "size" => 0, + "largest" => 0, + ); + + foreach($files as $key => $file) { + $size = filesize($file); + $fileList[] = array( + 'name' => $file, + 'size' => $this->getReadableFileSize($size) + ); + $fileTotals['size'] += $size; + if($size > $fileTotals['largest']) $fileTotals['largest'] = $size; + } + + $fileTotals['size'] = $this->getReadableFileSize($fileTotals['size']); + $fileTotals['largest'] = $this->getReadableFileSize($fileTotals['largest']); + $this->output['files'] = $fileList; + $this->output['fileTotals'] = $fileTotals; + } + + /*------------------------------------------- + MEMORY USAGE AND MEMORY AVAILABLE + -------------------------------------------*/ + + public function gatherMemoryData() { + $memoryTotals = array(); + $memoryTotals['used'] = $this->getReadableFileSize(memory_get_peak_usage()); + $memoryTotals['total'] = ini_get("memory_limit"); + $this->output['memoryTotals'] = $memoryTotals; + } + + /*-------------------------------------------------------- + QUERY DATA -- DATABASE OBJECT WITH LOGGING REQUIRED + ----------------------------------------------------------*/ + + public function gatherQueryData() { + $queryTotals = array(); + $queryTotals['count'] = 0; + $queryTotals['time'] = 0; + $queries = array(); + + if($this->db != '') { + $queryTotals['count'] += $this->db->queryCount; + foreach($this->db->queries as $key => $query) { + $query = $this->attemptToExplainQuery($query); + $queryTotals['time'] += $query['time']; + $query['time'] = $this->getReadableTime($query['time']); + $queries[] = $query; + } + } + + $queryTotals['time'] = $this->getReadableTime($queryTotals['time']); + $this->output['queries'] = $queries; + $this->output['queryTotals'] = $queryTotals; + } + + /*-------------------------------------------------------- + CALL SQL EXPLAIN ON THE QUERY TO FIND MORE INFO + ----------------------------------------------------------*/ + + function attemptToExplainQuery($query) { + try { + $sql = 'EXPLAIN '.$query['sql']; + $rs = $this->db->query($sql); + } + catch(Exception $e) {} + if($rs) { + $row = mysql_fetch_array($rs, MYSQL_ASSOC); + $query['explain'] = $row; + } + return $query; + } + + /*------------------------------------------- + SPEED DATA FOR ENTIRE PAGE LOAD + -------------------------------------------*/ + + public function gatherSpeedData() { + $speedTotals = array(); + $speedTotals['total'] = $this->getReadableTime(($this->getMicroTime() - $this->startTime)*1000); + $speedTotals['allowed'] = ini_get("max_execution_time"); + $this->output['speedTotals'] = $speedTotals; + } + + /*------------------------------------------- + HELPER FUNCTIONS TO FORMAT DATA + -------------------------------------------*/ + + function getMicroTime() { + $time = microtime(); + $time = explode(' ', $time); + return $time[1] + $time[0]; + } + + public function getReadableFileSize($size, $retstring = null) { + // adapted from code at http://aidanlister.com/repos/v/function.size_readable.php + $sizes = array('bytes', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + + if ($retstring === null) { $retstring = '%01.2f %s'; } + + $lastsizestring = end($sizes); + + foreach ($sizes as $sizestring) { + if ($size < 1024) { break; } + if ($sizestring != $lastsizestring) { $size /= 1024; } + } + if ($sizestring == $sizes[0]) { $retstring = '%01d %s'; } // Bytes aren't normally fractional + return sprintf($retstring, $size, $sizestring); + } + + public function getReadableTime($time) { + $ret = $time; + $formatter = 0; + $formats = array('ms', 's', 'm'); + if($time >= 1000 && $time < 60000) { + $formatter = 1; + $ret = ($time / 1000); + } + if($time >= 60000) { + $formatter = 2; + $ret = ($time / 1000) / 60; + } + $ret = number_format($ret,3,'.','') . ' ' . $formats[$formatter]; + return $ret; + } + + /*--------------------------------------------------------- + DISPLAY TO THE SCREEN -- CALL WHEN CODE TERMINATING + -----------------------------------------------------------*/ + + public function display($db = '', $master_db = '') { + $this->db = $db; + $this->master_db = $master_db; + $this->gatherConsoleData(); + $this->gatherFileData(); + $this->gatherMemoryData(); + $this->gatherQueryData(); + $this->gatherSpeedData(); + require_once($this->config.'display.php'); + displayPqp($this->output, OWA_PUBLIC_URL.'includes/pqp/'); + } + +} + +?> \ No newline at end of file diff --git a/includes/pqp/css/pQp.css b/includes/pqp/css/pQp.css new file mode 100644 index 000000000..8d9c68bb0 --- /dev/null +++ b/includes/pqp/css/pQp.css @@ -0,0 +1,406 @@ +/* - - - - - - - - - - - - - - - - - - - - - + + Title : PHP Quick Profiler CSS + Author : Designed by Kevin Hale. + URL : http://particletree.com/features/php-quick-profiler/ + + Last Updated : April 21, 2009 + +- - - - - - - - - - - - - - - - - - - - - */ + +.pQp{ + width:100%; + text-align:center; + position:fixed; + bottom:0; +} +* html .pQp{ + position:absolute; +} +.pQp *{ + margin:0; + padding:0; + border:none; +} +#pQp{ + margin:0 auto; + width:85%; + min-width:960px; + background-color:#222; + border:12px solid #000; + border-bottom:none; + font-family:"Lucida Grande", Tahoma, Arial, sans-serif; + -webkit-border-top-left-radius:15px; + -webkit-border-top-right-radius:15px; + -moz-border-radius-topleft:15px; + -moz-border-radius-topright:15px; +} +#pQp .pqp-box h3{ + font-weight:normal; + line-height:200px; + padding:0 15px; + color:#fff; +} +.pQp, .pQp td{ + color:#444; +} + +/* ----- IDS ----- */ + +#pqp-metrics{ + background:#000; + width:100%; +} +#pqp-console, #pqp-speed, #pqp-queries, #pqp-memory, #pqp-files{ + background:url(../images/overlay.gif); + border-top:1px solid #ccc; + height:200px; + overflow:auto; +} + +/* ----- Colors ----- */ + +.pQp .green{color:#588E13 !important;} +.pQp .blue{color:#3769A0 !important;} +.pQp .purple{color:#953FA1 !important;} +.pQp .orange{color:#D28C00 !important;} +.pQp .red{color:#B72F09 !important;} + +/* ----- Logic ----- */ + +#pQp, #pqp-console, #pqp-speed, #pqp-queries, #pqp-memory, #pqp-files{ + display:none; +} +.pQp .console, .pQp .speed, .pQp .queries, .pQp .memory, .pQp .files{ + display:block !important; +} +.pQp .console #pqp-console, .pQp .speed #pqp-speed, .pQp .queries #pqp-queries, +.pQp .memory #pqp-memory, .pQp .files #pqp-files{ + display:block; +} +.console td.green, .speed td.blue, .queries td.purple, .memory td.orange, .files td.red{ + background:#222 !important; + border-bottom:6px solid #fff !important; + cursor:default !important; +} + +.tallDetails #pQp .pqp-box{ + height:500px; +} +.tallDetails #pQp .pqp-box h3{ + line-height:500px; +} +.hideDetails #pQp .pqp-box{ + display:none !important; +} +.hideDetails #pqp-footer{ + border-top:1px dotted #444; +} +.hideDetails #pQp #pqp-metrics td{ + height:50px; + background:#000 !important; + border-bottom:none !important; + cursor:default !important; +} +.hideDetails #pQp var{ + font-size:18px; + margin:0 0 2px 0; +} +.hideDetails #pQp h4{ + font-size:10px; +} +.hideDetails .heightToggle{ + visibility:hidden; +} + +/* ----- Metrics ----- */ + +#pqp-metrics td{ + height:80px; + width:20%; + text-align:center; + cursor:pointer; + border:1px solid #000; + border-bottom:6px solid #444; + -webkit-border-top-left-radius:10px; + -moz-border-radius-topleft:10px; + -webkit-border-top-right-radius:10px; + -moz-border-radius-topright:10px; +} +#pqp-metrics td:hover{ + background:#222; + border-bottom:6px solid #777; +} +#pqp-metrics .green{ + border-left:none; +} +#pqp-metrics .red{ + border-right:none; +} + +#pqp-metrics h4{ + text-shadow:#000 1px 1px 1px; +} +.side var{ + text-shadow:#444 1px 1px 1px; +} + +.pQp var{ + font-size:23px; + font-weight:bold; + font-style:normal; + margin:0 0 3px 0; + display:block; +} +.pQp h4{ + font-size:12px; + color:#fff; + margin:0 0 4px 0; +} + +/* ----- Main ----- */ + +.pQp .main{ + width:80%; +} +*+html .pQp .main{ + width:78%; +} +* html .pQp .main{ + width:77%; +} +.pQp .main td{ + padding:7px 15px; + text-align:left; + background:#151515; + border-left:1px solid #333; + border-right:1px solid #333; + border-bottom:1px dotted #323232; + color:#FFF; +} +.pQp .main td, pre{ + font-family:Monaco, "Consolas", "Lucida Console", "Courier New", monospace; + font-size:11px; +} +.pQp .main td.alt{ + background:#111; +} +.pQp .main tr.alt td{ + background:#2E2E2E; + border-top:1px dotted #4E4E4E; +} +.pQp .main tr.alt td.alt{ + background:#333; +} +.pQp .main td b{ + float:right; + font-weight:normal; + color:#E6F387; +} +.pQp .main td:hover{ + background:#2E2E2E; +} + +/* ----- Side ----- */ + +.pQp .side{ + float:left; + width:20%; + background:#000; + color:#fff; + -webkit-border-bottom-left-radius:30px; + -moz-border-radius-bottomleft:30px; + text-align:center; +} +.pQp .side td{ + padding:10px 0 5px 0; + background:url(../images/side.png) repeat-y right; +} +.pQp .side var{ + color:#fff; + font-size:15px; +} +.pQp .side h4{ + font-weight:normal; + color:#F4FCCA; + font-size:11px; +} + +/* ----- Console ----- */ + +#pqp-console .side td{ + padding:12px 0; +} +#pqp-console .side td.alt1{ + background:#588E13; + width:51%; +} +#pqp-console .side td.alt2{ + background-color:#B72F09; +} +#pqp-console .side td.alt3{ + background:#D28C00; + border-bottom:1px solid #9C6800; + border-left:1px solid #9C6800; + -webkit-border-bottom-left-radius:30px; + -moz-border-radius-bottomleft:30px; +} +#pqp-console .side td.alt4{ + background-color:#3769A0; + border-bottom:1px solid #274B74; +} + +#pqp-console .main table{ + width:100%; +} +#pqp-console td div{ + width:100%; + overflow:hidden; +} +#pqp-console td.type{ + font-family:"Lucida Grande", Tahoma, Arial, sans-serif; + text-align:center; + text-transform: uppercase; + font-size:9px; + padding-top:9px; + color:#F4FCCA; + vertical-align:top; + width:40px; +} +.pQp .log-log td.type{ + background:#47740D !important; +} +.pQp .log-error td.type{ + background:#9B2700 !important; +} +.pQp .log-memory td.type{ + background:#D28C00 !important; +} +.pQp .log-speed td.type{ + background:#2B5481 !important; +} + +.pQp .log-log pre{ + color:#999; +} +.pQp .log-log td:hover pre{ + color:#fff; +} + +.pQp .log-memory em, .pQp .log-speed em{ + float:left; + font-style:normal; + display:block; + color:#fff; +} +.pQp .log-memory pre, .pQp .log-speed pre{ + float:right; + white-space: normal; + display:block; + color:#FFFD70; +} + +/* ----- Speed ----- */ + +#pqp-speed .side td{ + padding:12px 0; +} +#pqp-speed .side{ + background-color:#3769A0; +} +#pqp-speed .side td.alt{ + background-color:#2B5481; + border-bottom:1px solid #1E3C5C; + border-left:1px solid #1E3C5C; + -webkit-border-bottom-left-radius:30px; + -moz-border-radius-bottomleft:30px; +} + +/* ----- Queries ----- */ + +#pqp-queries .side{ + background-color:#953FA1; + border-bottom:1px solid #662A6E; + border-left:1px solid #662A6E; +} +#pqp-queries .side td.alt{ + background-color:#7B3384; +} +#pqp-queries .main b{ + float:none; +} +#pqp-queries .main em{ + display:block; + padding:2px 0 0 0; + font-style:normal; + color:#aaa; +} + +/* ----- Memory ----- */ + +#pqp-memory .side td{ + padding:12px 0; +} +#pqp-memory .side{ + background-color:#C48200; +} +#pqp-memory .side td.alt{ + background-color:#AC7200; + border-bottom:1px solid #865900; + border-left:1px solid #865900; + -webkit-border-bottom-left-radius:30px; + -moz-border-radius-bottomleft:30px; +} + +/* ----- Files ----- */ + +#pqp-files .side{ + background-color:#B72F09; + border-bottom:1px solid #7C1F00; + border-left:1px solid #7C1F00; +} +#pqp-files .side td.alt{ + background-color:#9B2700; +} + +/* ----- Footer ----- */ + +#pqp-footer{ + width:100%; + background:#000; + font-size:11px; + border-top:1px solid #ccc; +} +#pqp-footer td{ + padding:0 !important; + border:none !important; +} +#pqp-footer strong{ + color:#fff; +} +#pqp-footer a{ + color:#999; + padding:5px 10px; + text-decoration:none; +} +#pqp-footer .credit{ + width:20%; + text-align:left; +} +#pqp-footer .actions{ + width:80%; + text-align:right; +} +#pqp-footer .actions a{ + float:right; + width:auto; +} +#pqp-footer a:hover, #pqp-footer a:hover strong, #pqp-footer a:hover b{ + background:#fff; + color:blue !important; + text-decoration:underline; +} +#pqp-footer a:active, #pqp-footer a:active strong, #pqp-footer a:active b{ + background:#ECF488; + color:green !important; +} \ No newline at end of file diff --git a/includes/pqp/display.php b/includes/pqp/display.php new file mode 100644 index 000000000..9a1ee93bc --- /dev/null +++ b/includes/pqp/display.php @@ -0,0 +1,350 @@ + + +JAVASCRIPT; + +echo ''; + +} + +?> \ No newline at end of file diff --git a/includes/pqp/images/overlay.gif b/includes/pqp/images/overlay.gif new file mode 100644 index 000000000..8674fa544 Binary files /dev/null and b/includes/pqp/images/overlay.gif differ diff --git a/includes/pqp/images/side.png b/includes/pqp/images/side.png new file mode 100644 index 000000000..08243e2b5 Binary files /dev/null and b/includes/pqp/images/side.png differ diff --git a/includes/pqp/index.php b/includes/pqp/index.php new file mode 100644 index 000000000..05da7fca4 --- /dev/null +++ b/includes/pqp/index.php @@ -0,0 +1,178 @@ +profiler = new PhpQuickProfiler(PhpQuickProfiler::getMicroTime()); + } + + public function init() { + $this->sampleConsoleData(); + $this->sampleDatabaseData(); + $this->sampleMemoryLeak(); + $this->sampleSpeedComparison(); + } + + /*------------------------------------------- + EXAMPLES OF THE 4 CONSOLE FUNCTIONS + -------------------------------------------*/ + + public function sampleConsoleData() { + try { + Console::log('Begin logging data'); + Console::logMemory($this, 'PQP Example Class : Line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::log(array('Name' => 'Ryan', 'Last' => 'Campbell')); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logMemory($this, 'PQP Example Class : Line '.__LINE__); + Console::log('Ending log below with a sample error.'); + throw new Exception('Unable to write to log!'); + } + catch(Exception $e) { + Console::logError($e, 'Sample error logging.'); + } + } + + /*------------------------------------- + DATABASE OBJECT TO LOG QUERIES + --------------------------------------*/ + + public function sampleDatabaseData() { + /*$this->db = new MySqlDatabase( + 'your DB host', + 'your DB user', + 'your DB password'); + $this->db->connect(true); + $this->db->changeDatabase('your db name'); + + $sql = 'SELECT PostId FROM Posts WHERE PostId > 2'; + $rs = $this->db->query($sql); + + $sql = 'SELECT COUNT(PostId) FROM Posts'; + $rs = $this->db->query($sql); + + $sql = 'SELECT COUNT(PostId) FROM Posts WHERE PostId != 1'; + $rs = $this->db->query($sql);*/ + } + + /*----------------------------------- + EXAMPLE MEMORY LEAK DETECTED + ------------------------------------*/ + + public function sampleMemoryLeak() { + $ret = ''; + $longString = 'This is a really long string that when appended with the . symbol + will cause memory to be duplicated in order to create the new string.'; + for($i = 0; $i < 10; $i++) { + $ret = $ret . $longString; + Console::logMemory($ret, 'Watch memory leak -- iteration '.$i); + } + } + + /*----------------------------------- + POINT IN TIME SPEED MARKS + ------------------------------------*/ + + public function sampleSpeedComparison() { + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + Console::logSpeed('Time taken to get to line '.__LINE__); + } + + public function __destruct() { + $this->profiler->display($this->db); + } + +} + +$pqp = new PQPExample(); +$pqp->init(); + +?> + + + + + + +PHP Quick Profiler Demo + + + + + + + + + +
+

On this Page You Can See How to
Use the PHP Quick Profiler to...

+ + + + Return to Particletree. +
+ + + \ No newline at end of file diff --git a/includes/pqp/pqp.tpl b/includes/pqp/pqp.tpl new file mode 100644 index 000000000..699b4f684 --- /dev/null +++ b/includes/pqp/pqp.tpl @@ -0,0 +1,271 @@ + +{literal} + +{/literal} + + \ No newline at end of file diff --git a/modules/base/classes/browscap.php b/modules/base/classes/browscap.php index 2b9cf12e9..2d3c8ef89 100644 --- a/modules/base/classes/browscap.php +++ b/modules/base/classes/browscap.php @@ -61,8 +61,12 @@ class owa_browscap extends owa_base { function owa_browscap($ua = '') { - $this->owa_base(); + return owa_browscap::__construct($ua); + } + + function __construct($ua = '') { + parent::__construct(); // set user agent $this->ua = $ua; @@ -74,36 +78,38 @@ function owa_browscap($ua = '') { $this->browser = $this->lookup($this->ua); $this->e->debug('Browser Name : '. $this->browser->Browser); - return; } function robotCheck() { - if ($this->browser->Crawler == true): + if ($this->browser->Crawler === true) { $robot = true; - else: - if($this->robotRegexCheck() === true): + } elseif ($this->browser->Browser === "Default Browser") { + if($this->robotRegexCheck() === true) { $robot = true; - else: + } else { $robot = false; - endif; - endif; + } + } else { + $robot = false; + } owa_coreAPI::debug('Browscap Robot Check: '. $robot); return $robot; } function lookup($user_agent) { - if ($this->config['cache_objects'] == true): + if (owa_coreAPI::getSetting('base','cache_objects') === true) { + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $cache_obj = $this->cache->get('browscap', $this->ua); - endif; - - if (!empty($cache_obj)): + } + if (!empty($cache_obj)): + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); return $cache_obj; else: - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); // lookup from DB //$ua = owa_coreAPI::entityFactory('base.ua'); diff --git a/modules/base/classes/service.php b/modules/base/classes/service.php index b81471ab1..13af9528e 100644 --- a/modules/base/classes/service.php +++ b/modules/base/classes/service.php @@ -43,6 +43,7 @@ class owa_service extends owa_base { var $modules = array(); var $entities = array(); var $metrics = array(); + var $browscap; function owa_service() { @@ -50,7 +51,7 @@ function owa_service() { } function __construct() { - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); // setup request container $this->request = owa_coreAPI::requestContainerSingleton(); // setup settings @@ -64,6 +65,10 @@ function __construct() { return; } + function __destruct() { + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); + } + function initializeFramework() { if (!$this->isInit()) { @@ -76,6 +81,20 @@ function initializeFramework() { return; } + function setBrowscap($b) { + + $this->browscap = $b; + } + + function getBrowscap() { + + if (empty($this->browscap)) { + $this->browscap = owa_coreAPI::supportClassFactory('base', 'browscap', $this->request->getServerParam('HTTP_USER_AGENT')); + } + + return $this->browscap; + } + function _loadModules() { $am = owa_coreAPI::getActiveModules(); diff --git a/modules/base/classes/settings.php b/modules/base/classes/settings.php index c169289fa..0a46f5af5 100644 --- a/modules/base/classes/settings.php +++ b/modules/base/classes/settings.php @@ -575,7 +575,7 @@ function getDefaultConfig() { 'ws_timeout' => 10, 'is_active' => true, 'per_site_visitors' => false, - 'cache_objects' => false, + 'cache_objects' => true, 'log_named_users' => true, 'do_not_log_ips' => '', 'track_feed_links' => true, diff --git a/modules/base/processEvent.php b/modules/base/processEvent.php index c129f6cc0..33bfc3924 100644 --- a/modules/base/processEvent.php +++ b/modules/base/processEvent.php @@ -146,8 +146,8 @@ function pre() { } // Browser related properties - - $bcap = owa_coreAPI::supportClassFactory('base', 'browscap', $this->event->get('HTTP_USER_AGENT')); + $service = owa_coreAPI::serviceSingleton(); + $bcap = $service->getBrowscap(); $this->event->set('browser_type', $bcap->get('Browser')); $this->event->set('browser', $bcap->get('Browser') . ' ' . $bcap->get('Version')); diff --git a/modules/base/templates/invocation.tpl b/modules/base/templates/invocation.tpl index a4131cf22..409e4ebd4 100644 --- a/modules/base/templates/invocation.tpl +++ b/modules/base/templates/invocation.tpl @@ -5,6 +5,7 @@ diff --git a/modules/base/templates/js_helper_tags.tpl b/modules/base/templates/js_helper_tags.tpl index 542d5fc39..f2a953600 100644 --- a/modules/base/templates/js_helper_tags.tpl +++ b/modules/base/templates/js_helper_tags.tpl @@ -10,4 +10,4 @@ document.write(' \ No newline at end of file diff --git a/modules/base/templates/wrapper_default.tpl b/modules/base/templates/wrapper_default.tpl index a35fcf91c..2491e427b 100644 --- a/modules/base/templates/wrapper_default.tpl +++ b/modules/base/templates/wrapper_default.tpl @@ -5,15 +5,15 @@ Open Web Analytics - <?php echo $page_title;?> - setTemplate('head.tpl'));?> - setTemplate('css.tpl'));?> + getTemplatePath('base','head.tpl'));?> + getTemplatePath('base','css.tpl'));?> - setTemplate('header.tpl'));?> + getTemplatePath('base', 'header.tpl'));?> - setTemplate('msgs.tpl'));?> + getTemplatePath('base', 'msgs.tpl'));?> diff --git a/owa_base.php b/owa_base.php index d918be618..66ab962c9 100644 --- a/owa_base.php +++ b/owa_base.php @@ -79,7 +79,7 @@ function owa_base() { } function __construct() { - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $this->e = &owa_coreAPI::errorSingleton(); $this->c = &owa_coreAPI::configSingleton(); $this->config = &$this->c->fetch('base'); @@ -162,6 +162,10 @@ function _setArrayValues($array) { return; } + function __destruct() { + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); + } + } ?> \ No newline at end of file diff --git a/owa_caller.php b/owa_caller.php index 88c093355..1375078c3 100644 --- a/owa_caller.php +++ b/owa_caller.php @@ -294,6 +294,7 @@ function __destruct() { $total_time = $this->end_time - $this->start_time; $this->e->debug(sprintf('Total session time: %s',$total_time)); $this->e->debug("goodbye from OWA"); + owa_coreAPI::profileDisplay(); return; } diff --git a/owa_coreAPI.php b/owa_coreAPI.php index abf89db98..087f11fff 100644 --- a/owa_coreAPI.php +++ b/owa_coreAPI.php @@ -811,16 +811,20 @@ function logEvent($event_type, $message = '') { // do not log if the request is robotic $service = &owa_coreAPI::serviceSingleton(); - $bcap = owa_coreAPI::supportClassFactory('base', 'browscap', $service->request->getServerParam('HTTP_USER_AGENT')); - + $bcap = $service->getBrowscap(); + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); if (!owa_coreAPI::getSetting('base', 'log_robots')) { + if ($bcap->robotCheck()) { owa_coreAPI::debug("ABORTING: request appears to be from a robot"); owa_coreAPI::setRequestParam('is_robot', true); return; } + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); } + $service->setBrowscap($bcap); + // form event if one was not passed if (!is_a($message, 'owa_event')) { @@ -1143,6 +1147,38 @@ function getSitesList() { } + function profile($that = '', $function = '', $line = '', $msg = '') { + + if (defined('OWA_PROFILER')) { + if (OWA_PROFILER === true) { + + static $profiler; + + if (!class_exists('PhpQuickProfiler')) { + require_once(OWA_INCLUDE_DIR.'pqp/classes/PhpQuickProfiler.php'); + } + + if (empty($profiler)) { + $profiler = new PhpQuickProfiler(PhpQuickProfiler::getMicroTime(), OWA_INCLUDE_DIR.'pqp/'); + } + + $class = get_class($that); + Console::logSpeed($class."::$function - Line: $line - Msg: $msg"); + Console::logMemory($that, $class. "::$function - Line: $line"); + + return $profiler; + } + } + } + + function profileDisplay() { + $p = owa_coreAPI::profile(); + if ($p) { + $p->display(); + } + + } + } diff --git a/owa_db.php b/owa_db.php index 7855ac2fa..235c4a3ac 100644 --- a/owa_db.php +++ b/owa_db.php @@ -137,9 +137,12 @@ class owa_db extends owa_base { */ function owa_db() { - $this->owa_base(); + return owa_db::__construct(); + } + + function __construct() { - return; + return parent::__construct(); } /** @@ -381,7 +384,7 @@ function updateTable($table) { } function _insertQuery() { - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $params = $this->_fetchSqlParams('set_values'); $count = count($params); @@ -406,10 +409,12 @@ function _insertQuery() { endif; } - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $this->_setSql(sprintf(OWA_SQL_INSERT_ROW, $this->_sqlParams['table'], $sql_cols, $sql_values)); - - return $this->_query(); + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); + $ret = $this->_query(); + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); + return $ret; } diff --git a/owa_template.php b/owa_template.php index 8f33bebb1..c3da3d4ce 100644 --- a/owa_template.php +++ b/owa_template.php @@ -91,11 +91,43 @@ function _setTemplateDir($module) { $this->theme_template_dir = OWA_THEMES_DIR.$this->config['theme'].DIRECTORY_SEPARATOR; return; + } + + function getTemplatePath($module, $file) { + + $this->_setTemplateDir($module); + + if ($file == null) { + owa_coreAPI::error('No template file was specified.'); + return false; + } else { + // check module's local modification template Directory + if (file_exists($this->module_local_template_dir.$file)) { + $fullfile = $this->module_local_template_dir.$file; + + // check theme's template Directory + } elseif(file_exists($this->theme_template_dir.$file)) { + $fullfile = $this->theme_template_dir.$file; + + // check module's template directory + } elseif(file_exists($this->module_template_dir.$file)) { + $fullfile = $this->module_template_dir.$file; + + // throw error + } else { + $this->e->err(sprintf('%s was not found in any template directory.', $file)); + return false; + } + return $fullfile; + } + + + } /** * Set the template file - * + * @depricated * @param string $file */ function set_template($file = null) { @@ -125,6 +157,16 @@ function set_template($file = null) { return true; endif; } + + function setTemplateFile($module, $file) { + + //choose file + $filepath = $this->getTemplatePath($module, $file); + //set template + if ($filepath) { + $this->file = $filepath; + } + } /** * Truncate string diff --git a/plugins/db/owa_db_mysql.php b/plugins/db/owa_db_mysql.php index 9bcd17195..919017d98 100644 --- a/plugins/db/owa_db_mysql.php +++ b/plugins/db/owa_db_mysql.php @@ -81,10 +81,18 @@ class owa_db_mysql extends owa_db { * @access public */ function owa_db_mysql() { + + return owa_db_mysql::__construct(); + } - $this->owa_db(); + function __construct() { - return; + return parent::__construct(); + } + + function __destruct() { + + $this->close(); } function connect() { @@ -121,10 +129,12 @@ function connect() { function query($sql) { if ($this->connection_status == false): + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $this->connect(); + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); endif; - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $this->e->debug(sprintf('Query: %s', $sql)); $this->result = ''; @@ -133,9 +143,9 @@ function query($sql) { if (!empty($this->new_result)): mysql_free_result($this->new_result); endif; - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__, $sql); $result = @mysql_unbuffered_query($sql, $this->connection); - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); // Log Errors if (mysql_errno($this->connection)): $this->e->debug(sprintf('A MySQL error occured. Error: (%s) %s. Query: %s', @@ -143,7 +153,7 @@ function query($sql) { htmlspecialchars(mysql_error($this->connection)), $sql)); endif; - + owa_coreAPI::profile($this, __FUNCTION__, __LINE__); $this->new_result = $result; return $this->new_result;