$methods) { foreach ($methods as $method) { $count['classes']["$name"]["$method"] = 0; } } foreach ($filelist as $filename) { $fpath = $path . $filename; $file = file_get_contents($fpath); $count = fetch_calls($file, $elements['methods'], $elements['limiters']["$fpath"], $count); } print_r($count); // ################################################################### // ################################################################### // ################################################################### function fetch_listing($path, $basepath = '', $unset = 1) { static $filelist; if ($unset) { $filelist = array(); } if (substr($path, (strlen($path) - 1), 1) != '/') { $path .= '/'; } if ($handle = opendir($path)) { while (($file = readdir($handle)) !== false) { if (substr($file, 0, 1) != '.' && $file != 'CVS') { if (is_dir($path . $file)) { $filelist["$basepath"][] = "$file/"; fetch_listing("$path$file", "$basepath$file/", 0); } else { $filelist["$basepath"][] = $file; } } } closedir($handle); } return $filelist; } // ################################################################### function fetch_flat_listing($path) { $filelist = fetch_listing($path); foreach ($filelist as $basepath => $files) { foreach ($files as $file) { if (substr($file, (strlen($file) - 1), 1) != '/' && preg_match('#\.php$#', $file) && !strpos($basepath, '3rdparty') && !strpos($basepath, 'phpdoc') && !strpos($basepath, 'jpgraph')) { $flatlist[] = "./$basepath$file"; } } } return $flatlist; } // ################################################################### function fetch_calls($file, $methodlist, $limiters, $count) { static $objects, $extends; // strip comments $file = preg_replace('#/\*(.*?)\*/#s', '', $file); // tokens $tokens = token_get_all($file); // valid tokens static $validtokens, $othertokens; if (!is_array($validtokens)) { $validtokens = array('T_EXTENDS', 'T_FUNCTION', 'T_VARIABLE', 'T_STRING', 'T_NEW', 'T_DOUBLE_COLON', 'T_PAAMAYIM_NEKUDOTAYIM', 'T_OBJECT_OPERATOR'); $othertokens = array('(', ')', ';'); } // good tokens $gt = array(); // actual function calls $elements = array(); // remove invalid tokens $i = 0; foreach ($tokens as $id => $bit) { unset($tokens["$id"]); if (is_array($bit)) { if (in_array($token_name = token_name($bit[0]), $validtokens)) { $gt["$i"] = $bit; $gt["$i"][2] = $token_name; $gt["$i"][3] = $id; $i++; } } else { if (in_array($bit, $othertokens)) { $gt["$i"][0] = '-1'; $gt["$i"][1] = $bit; if ($bit == '(' || $bit == ')') { $gt["$i"][2] = 'P_' . ($bit == '(' ? 'OPEN' : 'CLOSE'); } else { $gt["$i"][2] = 'SC'; } $gt["$i"][3] = $id; $i++; } } } // get what we're looking for $triggers = array('new' => array(), 'popen' => array()); foreach ($methodlist['classes'] as $name => $methods) { $triggers['new'][] = $name; $triggers['popen'] = array_merge($triggers['popen'], $methods); } $triggers['popen'] = array_merge($triggers['popen'], $methodlist['functions']); // find a list of all the defined objects foreach ($gt as $id => $bit) { $prevbit = $gt[ $id - 1 ]; $nextbit = $gt[ $id + 1 ]; if ($bit[2] == 'T_NEW') { if ($prevbit[2] == 'T_VARIABLE' && $nextbit[2] == 'T_STRING' && in_array($nextbit[1], $triggers['new'])) { $objects["$prevbit[1]"] = $nextbit[1]; } } if ($bit[2] == 'T_EXTENDS') { if ($prevbit[2] == 'T_STRING' && $nextbit[2] == 'T_STRING' && in_array($prevbit[1], $triggers['new']) && in_array($prevbit[1], $triggers['new'])) { $extends["$prevbit[1]"] = $nextbit[1]; } } } // process tokens $incall = false; $root = 0; $stack = array(); $functlist = array(); foreach ($gt as $id => $bit) { $token = $bit[2]; $prevbit = $gt[ $id - 1 ]; $nextbit = $gt[ $id + 1 ]; // handle object calls if ($token == 'T_DOUBLE_COLON' || $token == 'T_PAAMAYIM_NEKUDOTAYIM' || $token == 'T_OBJECT_OPERATOR') { $classname = null; // find classname if (isset($objects["$prevbit[1]"])) { $classname = $objects["$prevbit[1]"]; } else if (in_array($prevbit[1], $triggers['new'])) { $classname = $prevbit[1]; } // we've got an extension! else if ($prevbit[1] == 'parent') { // find a class that the call could be in foreach ($limiters as $class => $limits) { if ($bit[3] > $limits[0] && $bit[3] < $limits[1]) { $refclass = $class; } } // get the parent of that object if (isset($methodlist['classes']["$refclass"])) { $tempclassname = $extends["$refclass"]; if (isset($methodlist['classes']["$tempclassname"])) { $classname = $tempclassname; } } } // we've got an inner-object call else if ($prevbit[1] == '$this') { // find a class that the call could be in foreach ($limiters as $class => $limits) { if ($bit[3] > $limits[0] && $bit[3] < $limits[1]) { $refclass = $class; } } if (isset($methodlist['classes']["$refclass"])) { $classname = $refclass; } } // object name validation if ($classname) { // method call validation if (in_array($nextbit[1], $methodlist['classes']["$classname"])) { $count['classes']["$classname"]["$nextbit[1]"]++; } } } // initial else if ($token == 'T_STRING' && $nextbit[2] == 'P_OPEN' && $prevbit[2] != 'T_FUNCTION' && $prevbit[2] != 'T_DOUBLE_COLON' && $prevbit[2] != 'T_PAAMAYIM_NEKUDOTAYIM' && $prevbit[2] != 'T_OBJECT_OPERATOR' && !in_array($bit[1], $triggers['new'])) { $incall = true; $root = $id; $functlist["$id"] = $bit[1]; } // parens in call else if ($token == 'P_OPEN' && $incall) { array_push($stack, $id); } // close parens in call else if ($token == 'P_CLOSE' && $incall) { // find the most recent function $end = array_pop($stack) - 1; // see if we can increment a function if (in_array($functlist["$end"], $methodlist['functions'])) { $count['functions'][ $functlist["$end"] ]++; } // we're done with all functions and the semi colon is next if (count($stack) == 0 && $nextbit[2] == 'SC') { $incall = false; } } } return $count; } // ################################################################### function fetch_methods($file) { // strip comments $file = preg_replace('#/\*(.*?)\*/#s', '', $file); // tokens $tokens = token_get_all($file); // valid tokens static $validtokens, $othertokensopen, $othertokensclose, $othertokens; if (!is_array($validtokens)) { $validtokens = array('T_CLASS', 'T_FUNCTION', 'T_STRING'); $othertokensopen = array('{', '${', 'T_CURLY_OPEN', 'T_DOLLAR_OPEN_CURLY_BRACES'); $othertokensclose = array('}'); $othertokens = array_merge($othertokensopen, $othertokensclose); } // good tokens $gt = array(); // actual named items $elements = array(); // remove invalid tokens $i = 0; foreach ($tokens as $id => $bit) { unset($tokens["$id"]); if (is_array($bit)) { // if we have a T_CLASS, T_FUNCTION, or T_STRING if (in_array($token_name = token_name($bit[0]), $validtokens)) { $gt["$i"] = $bit; $gt["$i"][2] = $token_name; $gt["$i"][3] = $id; $i++; } // special open { else if (in_array($token_name = token_name($bit[0]), $othertokens)) { $gt["$i"][0] = '-1'; $gt["$i"][1] = $bit[1]; $gt["$i"][2] = 'B_' . (in_array($token_name, $othertokensopen) ? 'OPEN' : 'CLOSE'); $gt["$i"][3] = $id; $i++; } } // if we have an opening or closing brace else if (in_array($bit, $othertokens)) { // make up some token information $gt["$i"][0] = '-1'; $gt["$i"][1] = $bit; $gt["$i"][2] = 'B_' . (in_array($bit, $othertokensopen) ? 'OPEN' : 'CLOSE'); $gt["$i"][3] = $id; $i++; } } // process tokens $elements = array('classes' => array(), 'functions' => array()); $inclass = false; $infunction = false; $class = ''; $stack = array(); foreach ($gt as $id => $bit) { $token = $bit[2]; $nextbit = $gt[ $id + 1 ]; if ($token == 'T_CLASS') { $inclass = true; $class = $nextbit[1]; $elements['classes']["$class"] = array(); $limiters["$class"][0] = $bit[3]; } else if ($token == 'T_FUNCTION') { $infunction = true; if ($inclass) { $elements['classes']["$class"][] = $nextbit[1]; } else { $elements['functions'][] = $nextbit[1]; } } else if ($token == 'B_OPEN') { array_push($stack, $id); } else if ($token == 'B_CLOSE') { array_pop($stack); // breaking out of a method if ($inclass && $infunction) { if (count($stack) < 2) { $infunction = false; } } // breaking out of a class else if ($inclass && !$infunction) { if (count($stack) < 1) { $inclass = false; $limiters["$class"][1] = $bit[3]; } } // breaking out of a function else if (!$inclass && $infunction) { if (count($stack) == 0) { $infunction = false; } } } $lastbit = $bit; } return array('methods' => $elements, 'limiters' => $limiters); } ?>