Skip to content

Instantly share code, notes, and snippets.

@jderusse
Last active August 29, 2015 14:15
Show Gist options
  • Save jderusse/32a9d0b105e359f322af to your computer and use it in GitHub Desktop.
Save jderusse/32a9d0b105e359f322af to your computer and use it in GitHub Desktop.
melody-extensions
<?php
<<<CONFIG
packages:
- "nikic/php-parser"
CONFIG;
/**
* Browse the current working directory to search called function.
* Then render the list of functions grouped by extension name.
*
* Usage :
* $ cd /path/to/your/project
* $ melody run 32a9d0b105e359f322af [ReportType]
*
* With ReportType:
* - "full" Display files group by function group by extension
* - "function" Display function group by extension
* - "file" Display files group by extension
* - "extension" Display extensions
**/
$reportName = ucfirst(strtolower(isset($argv[1]) ? $argv[1] : 'File'));
$reportClass = $reportName . 'Report';
if (!class_exists($reportClass)) {
die('Unkown repport ' . $reportName . PHP_EOL);
}
class FunctionCallVisitor extends PhpParser\NodeVisitorAbstract
{
private $collector;
public function __construct(Collector $collector)
{
$this->collector = $collector;
}
public function leaveNode(PhpParser\Node $node)
{
if ($node instanceof PhpParser\Node\Stmt\Function_) {
$this->collector->addStmtFunction(strtolower($node->name));
} elseif ($node instanceof PhpParser\Node\Expr\FuncCall && $node->name instanceof PhpParser\Node\Name) {
$this->collector->addCalledFunction(strtolower($node->name));
}
}
}
class Called
{
public $file;
public $functionName;
public function __construct($file, $functionName)
{
$this->file = $file;
$this->functionName = $functionName;
}
}
class Collector
{
private $currentFile;
private $stmtFunctions = array();
private $called = array();
public function setCurrentFile($file)
{
$this->currentFile = $file;
}
public function addCalledFunction($functionName)
{
if (!in_array($functionName, $this->stmtFunctions)) {
$this->called[sprintf('%s :: %s', $this->currentFile, $functionName)] = new Called($this->currentFile, $functionName);
}
}
public function addStmtFunction($functionName)
{
if (!in_array($functionName, $this->stmtFunctions)) {
$this->stmtFunctions[] = $functionName;
$this->called = array_filter($this->called, function ($called) use ($functionName) {
return $called->functionName !== $functionName;
});
}
}
public function getData()
{
$phpFunctions = array();
foreach (get_loaded_extensions() as $extension) {
if (false !== $functions = get_extension_funcs($extension)) {
foreach ($functions as $function) {
$phpFunctions[$function] = $extension;
}
}
}
$data = array();
foreach ($this->called as $called) {
if (isset($phpFunctions[$called->functionName])) {
$data[$phpFunctions[$called->functionName]][$called->functionName][] = $called->file;
} else {
$data['?'][$called->functionName][] = $called->file;
}
}
ksort($data);
foreach ($data as & $extensionData) {
ksort($extensionData);
foreach ($extensionData as & $functionData) {
sort($functionData);
}
}
return $data;
}
}
abstract class Report
{
protected $collectedData;
public function __construct(array $collectedData, array $excludedExtension = array('Core', 'standard'))
{
foreach ($excludedExtension as $extension) {
unset($collectedData[$extension]);
}
$this->collectedData = $collectedData;
}
abstract public function render();
}
class FullReport extends Report
{
public function render()
{
foreach ($this->collectedData as $extension => $extensionData) {
echo $extension, PHP_EOL;
foreach ($extensionData as $functionName => $functionData) {
echo ' ', $functionName, PHP_EOL;
foreach ($functionData as $fileName) {
echo ' ', $fileName, PHP_EOL;
}
}
}
}
}
class FileReport extends Report
{
public function render()
{
foreach ($this->collectedData as $extension => $extensionData) {
echo $extension, PHP_EOL;
if ($extension === '?') {
foreach ($extensionData as $functionName => $functionData) {
echo ' ', $functionName, PHP_EOL;
foreach ($functionData as $fileName) {
echo ' ', $fileName, PHP_EOL;
}
}
} else {
$files = call_user_func_array('array_merge', array_values($extensionData));
array_unique($files);
foreach ($files as $fileName) {
echo ' ', $fileName, PHP_EOL;
}
}
}
}
}
class FunctionReport extends Report
{
public function render()
{
foreach ($this->collectedData as $extension => $extensionData) {
echo $extension, PHP_EOL;
foreach (array_keys($extensionData) as $functionName) {
echo ' ', $functionName, PHP_EOL;
}
}
}
}
class ExtensionReport extends Report
{
public function render()
{
foreach ($this->collectedData as $extension => $extensionData) {
echo $extension, PHP_EOL;
if ($extension === '?') {
foreach (array_keys($extensionData) as $functionName) {
echo ' ', $functionName, PHP_EOL;
}
}
}
}
}
$collector = new Collector();
$inDir = getcwd();
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new FunctionCallVisitor($collector));
// iterate over all .php files in the directory
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($inDir));
$files = new RegexIterator($files, '/\.php$/');
function format_size($size)
{
$unit=array('o', 'kio', 'Mio', 'Gio', 'Tio', 'Pio');
return sprintf('mem %01.1f %s', $size/pow(1024, ($i=floor(log($size, 1024)))), $unit[$i]);
}
foreach ($files as $file) {
echo sprintf("\r%-64.64s %15s", substr($file, strlen($inDir)), format_size(memory_get_usage()));
$collector->setCurrentFile((string) $file);
try {
$code = file_get_contents($file);
$stmts = $parser->parse($code);
// traverse
$stmts = $traverser->traverse($stmts);
} catch (PhpParser\Error $e) {
echo PHP_EOL, $file, 'Parse Error: ', $e->getMessage(), PHP_EOL;
}
}
echo sprintf("\r%-80.80s\r", '');
$report = new $reportClass($collector->getData());
$report->render();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment