2 ini_set('display_errors', "On");
4 require_once 'PEAR.php';
6 if(!function_exists('scandir'))
8 function scandir($dir, $sortorder = 0)
12 $dirlist = opendir($dir);
14 while( ($file = readdir($dirlist)) !== false)
22 ($sortorder == 0) ?
asort($files) : arsort($files);
36 * Command-line options parsing class.
38 * @author Andrei Zmievski <andrei@php.net>
41 class Console_Getopt
{
43 * Parses the command-line options.
45 * The first parameter to this function should be the list of command-line
46 * arguments without the leading reference to the running program.
48 * The second parameter is a string of allowed short options. Each of the
49 * option letters can be followed by a colon ':' to specify that the option
50 * requires an argument, or a double colon '::' to specify that the option
51 * takes an optional argument.
53 * The third argument is an optional array of allowed long options. The
54 * leading '--' should not be included in the option name. Options that
55 * require an argument should be followed by '=', and options that take an
56 * option argument should be followed by '=='.
58 * The return value is an array of two elements: the list of parsed
59 * options and the list of non-option command-line arguments. Each entry in
60 * the list of parsed options is a pair of elements - the first one
61 * specifies the option, and the second one specifies the option argument,
64 * Long and short options can be mixed.
66 * Most of the semantics of this function are based on GNU getopt_long().
68 * @param array $args an array of command-line arguments
69 * @param string $short_options specifies the list of allowed short options
70 * @param array $long_options specifies the list of allowed long options
72 * @return array two-element array containing the list of parsed options and
73 * the non-option arguments
78 function getopt2($args, $short_options, $long_options = null)
80 return Console_Getopt
::doGetopt(2, $args, $short_options, $long_options);
84 * This function expects $args to start with the script name (POSIX-style).
85 * Preserved for backwards compatibility.
88 function getopt($args, $short_options, $long_options = null)
90 return Console_Getopt
::doGetopt(1, $args, $short_options, $long_options);
94 * The actual implementation of the argument parsing code.
96 function doGetopt($version, $args, $short_options, $long_options = null)
98 // in case you pass directly readPHPArgv() as the first arg
99 if (PEAR
::isError($args)) {
103 return array(array(), array());
108 settype($args, 'array');
115 * Preserve backwards compatibility with callers that relied on
116 * erroneous POSIX fix.
119 if (isset($args[0]{0}) && $args[0]{0} != '-') {
125 while (list($i, $arg) = each($args)) {
127 /* The special element '--' means explicit end of
128 options. Treat the rest of the arguments as non-options
131 $non_opts = array_merge($non_opts, array_slice($args, $i +
1));
135 if ($arg{0} != '-' ||
(strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
136 $non_opts = array_merge($non_opts, array_slice($args, $i));
138 } elseif (strlen($arg) > 1 && $arg{1} == '-') {
139 $error = Console_Getopt
::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
140 if (PEAR
::isError($error))
143 $error = Console_Getopt
::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
144 if (PEAR
::isError($error))
149 return array($opts, $non_opts);
156 function _parseShortOption($arg, $short_options, &$opts, &$args)
158 for ($i = 0; $i < strlen($arg); $i++
) {
162 /* Try to find the short option in the specifier string. */
163 if (($spec = strstr($short_options, $opt)) === false ||
$arg{$i} == ':')
165 return PEAR
::raiseError("Console_Getopt: unrecognized option -- $opt");
168 if (strlen($spec) > 1 && $spec{1} == ':') {
169 if (strlen($spec) > 2 && $spec{2} == ':') {
170 if ($i +
1 < strlen($arg)) {
171 /* Option takes an optional argument. Use the remainder of
172 the arg string if there is anything left. */
173 $opts[] = array($opt, substr($arg, $i +
1));
177 /* Option requires an argument. Use the remainder of the arg
178 string if there is anything left. */
179 if ($i +
1 < strlen($arg)) {
180 $opts[] = array($opt, substr($arg, $i +
1));
182 } else if (list(, $opt_arg) = each($args))
183 /* Else use the next argument. */;
185 return PEAR
::raiseError("Console_Getopt: option requires an argument -- $opt");
189 $opts[] = array($opt, $opt_arg);
197 function _parseLongOption($arg, $long_options, &$opts, &$args)
199 @list
($opt, $opt_arg) = explode('=', $arg);
200 $opt_len = strlen($opt);
202 for ($i = 0; $i < count($long_options); $i++
) {
203 $long_opt = $long_options[$i];
204 $opt_start = substr($long_opt, 0, $opt_len);
206 /* Option doesn't match. Go on to the next one. */
207 if ($opt_start != $opt)
210 $opt_rest = substr($long_opt, $opt_len);
212 /* Check that the options uniquely matches one of the allowed
214 if ($opt_rest != '' && $opt{0} != '=' &&
215 $i +
1 < count($long_options) &&
216 $opt == substr($long_options[$i+
1], 0, $opt_len)) {
217 return PEAR
::raiseError("Console_Getopt: option --$opt is ambiguous");
220 if (substr($long_opt, -1) == '=') {
221 if (substr($long_opt, -2) != '==') {
222 /* Long option requires an argument.
223 Take the next argument if one wasn't specified. */;
224 if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
225 return PEAR
::raiseError("Console_Getopt: option --$opt requires an argument");
228 } else if ($opt_arg) {
229 return PEAR
::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
232 $opts[] = array('--' . $opt, $opt_arg);
236 return PEAR
::raiseError("Console_Getopt: unrecognized option --$opt");
240 * Safely read the $argv PHP array across different PHP configurations.
241 * Will take care on register_globals and register_argc_argv ini directives
244 * @return mixed the $argv PHP array or PEAR error if not registered
246 function readPHPArgv()
249 if (!is_array($argv)) {
250 if (!@is_array
($_SERVER['argv'])) {
251 if (!@is_array
($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
252 return PEAR
::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
254 return $GLOBALS['HTTP_SERVER_VARS']['argv'];
256 return $_SERVER['argv'];
265 * Profiler adapted from Pear::APD's pprofp script. Not quite there yet, I need
266 * to get this to accept a similar list of arguments as the script does,
267 * and process them the same way. Also make sure that the file being loaded
268 * is the right one. Also support multiple pids used in one page load (up to 4 so far).
269 * Then output all this in a nicely formatted table.
281 * Concatenates all the pprof files generated by apd_set_pprof_trace()
282 * and returns the resulting string, which can then be processed by
284 * It also deletes these files once finished, in order to limit
285 * cluttering of the filesystem. This can be switched off by
286 * providing "false" as the only argument to this function.
288 * WARNING: If you switch cleanup off, profiling data will
289 * accumulate from one pageload to the next.
291 * @param boolean $cleanup Whether to delete pprof files or not.
292 * @return String Profiling raw data
294 function _get_pprofp($cleanup = true)
297 // List all files under our temporary directory
298 $tempdir = $CFG->dataroot
. '/temp/profile/' . $USER->id
;
299 if ($files = scandir($tempdir)) {
300 // Concatenate the files
303 print "Error: Profiler could not read the directory $tempdir.";
308 // Return a handle to the resulting file
311 if(($DATA = fopen($dataFile, "r")) == FALSE) {
312 return "Failed to open $dataFile for reading\n";
319 * Returns profiling information gathered using APD functions.
320 * Accepts a numerical array of command-line arguments.
322 * @usage Profiler::get_profiling($args)
324 * -a Sort by alphabetic names of subroutines.
325 * -l Sort by number of calls to subroutines
326 * -m Sort by memory used in a function call.
327 * -r Sort by real time spent in subroutines.
328 * -R Sort by real time spent in subroutines (inclusive of child calls).
329 * -s Sort by system time spent in subroutines.
330 * -S Sort by system time spent in subroutines (inclusive of child calls).
331 * -u Sort by user time spent in subroutines.
332 * -U Sort by user time spent in subroutines (inclusive of child calls).
333 * -v Sort by average amount of time spent in subroutines.
334 * -z Sort by user+system time spent in subroutines. (default)
337 * -c Display Real time elapsed alongside call tree.
338 * -i Suppress reporting for php builtin functions
339 * -O <cnt> Specifies maximum number of subroutines to display. (default 15)
340 * -t Display compressed call tree.
341 * -T Display uncompressed call tree.
343 * Example array: array('-a', '-l');
346 * @return String Profiling info
348 function get_profiling($args)
350 $con = new Console_Getopt
;
353 $shortoptions = 'acg:hiIlmMrRsStTuUO:vzZ';
354 $retval = $con->getopt( $args, $shortoptions);
355 if(is_object($retval)) {
360 foreach ($retval[0] as $kv_array) {
361 $opt[$kv_array[0]] = $kv_array[1];
364 $DATA = Profiler
::_get_pprofp();
367 $this->parse_info('HEADER', $DATA, $cfg);
369 $callstack = array();
372 $file_hash = array();
373 $this->mem
= array();
378 $this->c_stimes
= array();
379 $this->c_utimes
= array();
381 $this->stimes
= array();
382 $this->utimes
= array();
388 $symbol_hash = array();
389 $symbol_type = array();
391 while($line = fgets($DATA)) {
392 $line = rtrim($line);
393 if(preg_match("/^END_TRACE/", $line)){
396 list($token, $data) = preg_split("/ /",$line, 2);
398 list ($index, $file) = preg_split("/ /", $data, 2);
399 $file_hash[$index] = $file;
403 list ($index, $name, $type) = preg_split("/ /", $data, 3);
404 $symbol_hash[$index] = $name;
405 $symbol_type[$index] = $type;
409 list($index, $file, $line) = preg_split("/ /",$data, 3);
410 if(array_key_exists('i',$opt) && $symbol_type[$index] == 1) {
414 $calls[$index_cur]++
;
415 array_push($callstack, $index_cur);
416 if(array_key_exists('T', $opt)) {
417 if(array_key_exists('c', $opt)) {
418 $retstring .= sprintf("%2.02f ", $rtotal/1000000);
420 $retstring .= str_repeat(" ", $indent_cur).$symbol_hash[$index_cur]."\n";
421 if(array_key_exists('m', $opt)) {
422 $retstring .= str_repeat(" ", $indent_cur)."C: $file_hash[$file]:$line M: $memory\n";
425 elseif(array_key_exists('t', $opt)) {
426 if ( $indent_last == $indent_cur && $index_last == $index_cur ) {
431 $repstr = ' ('.++
$repcnt.'x)';
433 if(array_key_exists('c', $opt)) {
434 $retstring .= sprintf("%2.02f ", $rtotal/1000000);
436 $retstring .= str_repeat(" ", $indent_last).$symbol_hash[$index_last].$repstr."\n";
437 if(array_key_exists('m', $opt)) {
438 $retstring .= str_repeat(" ", $indent_cur)."C: $file_hash[$file_last]:$line_last M: $memory\n";
442 $index_last = $index_cur;
443 $indent_last = $indent_cur;
452 list($file_no, $line_no, $ut, $st, $rt) = preg_split("/ /", $data);
453 $top = array_pop($callstack);
454 $this->utimes
[$top] +
= $ut;
456 $this->stimes
[$top] +
= $st;
458 $rtimes[$top] +
= $rt;
460 array_push($callstack, $top);
461 foreach ($callstack as $stack_element) {
462 $this->c_utimes
[$stack_element] +
= $ut;
463 $this->c_stimes
[$stack_element] +
= $st;
464 $c_rtimes[$stack_element] +
= $rt;
469 list ($index, $memory) = preg_split("/ /", $data, 2);
470 if(array_key_exists('i',$opt) && $symbol_type[$index] == 1)
474 $this->mem
[$index] +
= ($memory - $last_memory);
475 $last_memory = $memory;
477 $tmp = array_pop($callstack);
481 $this->parse_info('FOOTER', $DATA, $cfg);
483 if(array_key_exists('l', $opt)) { $sort = 'by_calls'; }
484 if(array_key_exists('m', $opt)) { $sort = 'by_mem'; }
485 if(array_key_exists('a', $opt)) { $sort = 'by_name'; }
486 if(array_key_exists('v', $opt)) { $sort = 'by_avgcpu'; }
487 if(array_key_exists('r', $opt)) { $sort = 'by_rtime'; }
488 if(array_key_exists('R', $opt)) { $sort = 'by_c_rtime'; }
489 if(array_key_exists('s', $opt)) { $sort = 'by_stime'; }
490 if(array_key_exists('S', $opt)) { $sort = 'by_c_stime'; }
491 if(array_key_exists('u', $opt)) { $sort = 'by_utime'; }
492 if(array_key_exists('U', $opt)) { $sort = 'by_c_utime'; }
493 if(array_key_exists('Z', $opt)) { $sort = 'by_c_time'; }
494 if( !count($symbol_hash)) {
498 $retstring .= sprintf("
500 Total Elapsed Time = %4.2f
501 Total System Time = %4.2f
502 Total User Time = %4.2f
503 ", $cfg['caller'], $rtotal/1000000, $stotal/1000000, $utotal/1000000);
506 Real User System secs/ cumm
507 %Time (excl/cumm) (excl/cumm) (excl/cumm) Calls call s/call Memory Usage Name
508 --------------------------------------------------------------------------------------\n";
514 uksort($symbol_hash, $sort);
515 foreach (array_keys($symbol_hash) as $j) {
516 if(array_key_exists('i', $opt) && $symbol_type[$j] == 1) {
519 if ($l++
< $opt['O']) {
520 $pcnt = 100*($this->stimes
[$j] +
$this->utimes
[$j])/($utotal +
$stotal +
$itotal);
521 $c_pcnt = 100* ($this->c_stimes
[$j] +
$this->c_utimes
[$j])/($utotal +
$stotal +
$itotal);
522 $rsecs = $rtimes[$j]/1000000;
523 $ssecs = $this->stimes
[$j]/1000000;
524 $usecs = $this->utimes
[$j]/1000000;
525 $c_rsecs = $c_rtimes[$j]/1000000;
526 $c_ssecs = $this->c_stimes
[$j]/1000000;
527 $c_usecs = $this->c_utimes
[$j]/1000000;
528 $ncalls = $calls[$j];
529 if(array_key_exists('z', $opt)) {
530 $percall = ($usecs +
$ssecs)/$ncalls;
531 $cpercall = ($c_usecs +
$c_ssecs)/$ncalls;
532 if($utotal +
$stotal) {
533 $pcnt = 100*($this->stimes
[$j] +
$this->utimes
[$j])/($utotal +
$stotal);
539 if(array_key_exists('Z', $opt)) {
540 $percall = ($usecs +
$ssecs)/$ncalls;
541 $cpercall = ($c_usecs +
$c_ssecs)/$ncalls;
542 if($utotal +
$stotal) {
543 $pcnt = 100*($this->c_stimes
[$j] +
$this->c_utimes
[$j])/($utotal +
$stotal);
549 if(array_key_exists('r', $opt)) {
550 $percall = ($rsecs)/$ncalls;
551 $cpercall = ($c_rsecs)/$ncalls;
553 $pcnt = 100*$rtimes[$j]/$rtotal;
559 if(array_key_exists('R', $opt)) {
560 $percall = ($rsecs)/$ncalls;
561 $cpercall = ($c_rsecs)/$ncalls;
563 $pcnt = 100*$c_rtimes[$j]/$rtotal;
569 if(array_key_exists('u', $opt)) {
570 $percall = ($usecs)/$ncalls;
571 $cpercall = ($c_usecs)/$ncalls;
573 $pcnt = 100*$this->utimes
[$j]/$utotal;
579 if(array_key_exists('U', $opt)) {
580 $percall = ($usecs)/$ncalls;
581 $cpercall = ($c_usecs)/$ncalls;
583 $pcnt = 100*$this->c_utimes
[$j]/$utotal;
589 if(array_key_exists('s', $opt)) {
590 $percall = ($ssecs)/$ncalls;
591 $cpercall = ($c_ssecs)/$ncalls;
593 $pcnt = 100*$this->stimes
[$j]/$stotal;
599 if(array_key_exists('S', $opt)) {
600 $percall = ($ssecs)/$ncalls;
601 $cpercall = ($c_ssecs)/$ncalls;
603 $pcnt = 100*$this->c_stimes
[$j]/$stotal;
609 // $cpercall = ($c_usecs + $c_ssecs)/$ncalls;
610 $mem_usage = $this->mem
[$j];
611 $name = $symbol_hash[$j];
612 $retstring .= sprintf("%3.01f %2.02f %2.02f %2.02f %2.02f %2.02f %2.02f %4d %2.04f %2.04f %12d %s\n",
613 $pcnt, $rsecs, $c_rsecs, $usecs, $c_usecs, $ssecs, $c_ssecs, $ncalls, $percall, $cpercall, $mem_usage, $name);
622 Profiler::get_profiling(\$args)
624 -a Sort by alphabetic names of subroutines.
625 -l Sort by number of calls to subroutines
626 -m Sort by memory used in a function call.
627 -r Sort by real time spent in subroutines.
628 -R Sort by real time spent in subroutines (inclusive of child calls).
629 -s Sort by system time spent in subroutines.
630 -S Sort by system time spent in subroutines (inclusive of child calls).
631 -u Sort by user time spent in subroutines.
632 -U Sort by user time spent in subroutines (inclusive of child calls).
633 -v Sort by average amount of time spent in subroutines.
634 -z Sort by user+system time spent in subroutines. (default)
637 -c Display Real time elapsed alongside call tree.
638 -i Suppress reporting for php builtin functions
639 -O <cnt> Specifies maximum number of subroutines to display. (default 15)
640 -t Display compressed call tree.
641 -T Display uncompressed call tree.
647 function parse_info($tag, $datasource, &$cfg) {
648 while($line = fgets($datasource)) {
649 $line = rtrim($line);
650 if(preg_match("/^END_$tag$/", $line)) {
653 if(preg_match("/(\w+)=(.*)/", $line, $matches)) {
654 $cfg[$matches[1]] = $matches[2];
659 function num_cmp($a, $b) {
660 if (intval($a) > intval($b)) { return 1;}
661 elseif(intval($a) < intval($b)) { return -1;}
665 function by_time($a,$b) {
666 return $this->num_cmp(($this->stimes
[$b] +
$this->utimes
[$b]),($this->stimes
[$a] +
$this->utimes
[$a]));
669 function by_c_time($a,$b) {
670 return $this->num_cmp(($this->c_stimes
[$b] +
$this->c_utimes
[$b]),($this->c_stimes
[$a] +
$this->c_utimes
[$a]));
673 function by_avgcpu($a,$b) {
674 return $this->num_cmp(($this->stimes
[$b] +
$this->utimes
[$b])/$this->calls
[$b],($this->stimes
[$a] +
$this->utimes
[$a])/$this->calls
[$a]);
677 function by_calls($a, $b) {
678 return $this->num_cmp($this->calls
[$b], $this->calls
[$a]);
681 function by_rtime($a,$b) {
682 return $this->num_cmp($this->rtimes
[$b], $this->rtimes
[$a]);
685 function by_c_rtime($a,$b) {
686 return $this->num_cmp($this->c_rtimes
[$b], $this->c_rtimes
[$a]);
689 function by_stime($a,$b) {
690 return $this->num_cmp($this->stimes
[$b], $this->stimes
[$a]);
693 function by_c_stime($a,$b) {
694 return $this->num_cmp($this->c_stimes
[$b], $this->c_stimes
[$a]);
697 function by_utime($a,$b) {
698 return $this->num_cmp($this->utimes
[$b], $this->utimes
[$a]);
701 function by_c_utime($a,$b) {
702 return $this->num_cmp($this->c_utimes
[$b], $this->c_utimes
[$a]);
705 function by_mem($a, $b) {
706 return $this->num_cmp($this->mem
[$b], $this->mem
[$a]);