Skip to content

Commit

Permalink
Export the names of the functions used in each element
Browse files Browse the repository at this point in the history
  • Loading branch information
JDGrimes committed Mar 24, 2014
1 parent 49b9d28 commit 133eda2
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 28 deletions.
67 changes: 45 additions & 22 deletions lib/class-file-reflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,28 @@
*/
class File_Reflector extends FileReflector {
/**
* List of hooks defined in global scope in this file.
* List of elements used in global scope in this file, indexed by element type.
*
* @var \WP_Parser\Hook_Reflector[]
* @var array {
* @type \WP_Parser\Hook_Reflector[] $hooks The action and filters.
* @type \WP_Parser\Function_Call_Reflector[] $functions The functions called.
* }
*/
public $hooks = array();
public $uses = array();

/**
* List of hooks defined in the current node scope.
* List of elements used in the current node scope, indexed by element type.
*
* @var \WP_Parser\Hook_Reflector[]
* @var array {@see \WP_Parser\File_Reflector::$uses}
*/
protected $hooks_queue = array();
protected $uses_queue = array();

/**
* List of hooks defined in the current class scope, indexed by method.
* List of elements used in the current class scope, indexed by method.
*
* @var \WP_Parser\Hook_Reflector[]
* @var array[][] {@see \WP_Parser\File_Reflector::$uses}
*/
protected $method_hooks_queue = array();
protected $method_uses_queue = array();

/**
* Stack of classes/methods/functions currently being parsed.
Expand All @@ -58,7 +61,9 @@ class File_Reflector extends FileReflector {
* We also check function calls to see if there are any actions or hooks. If
* there are, they are added to the file's hooks if in the global scope, or if
* we are in a function/method, they are added to the queue. They will be
* assinged to the function by leaveNode().
* assinged to the function by leaveNode(). We also check for any other function
* calls and treat them similarly, so that we can export a list of functions
* used by each element.
*
* Finally, we pick up any docblocks for nodes that usually aren't documentable,
* so they can be assigned to the hooks to which they may belong.
Expand All @@ -74,8 +79,21 @@ public function enterNode( \PHPParser_Node $node ) {
array_push( $this->location, $node );
break;

// Parse out hook definitions and add them to the queue.
// Parse out hook definitions and function calls and add them to the queue.
case 'Expr_FuncCall':
$function = new \WP_Parser\Function_Call_Reflector( $node, $this->context );

/*
* If the function call is in the global scope, add it to the
* file's function calls. Otherwise, add it to the queue so it
* can be added to the correct node when we leave it.
*/
if ( $this === $this->getLocation() ) {
$this->uses['functions'][] = $function;
} else {
$this->uses_queue['functions'][] = $function;
}

if ( $this->isFilter( $node ) ) {
if ( $this->last_doc && ! $node->getDocComment() ) {
$node->setAttribute( 'comments', array( $this->last_doc ) );
Expand All @@ -90,9 +108,9 @@ public function enterNode( \PHPParser_Node $node ) {
* the correct node when we leave it.
*/
if ( $this === $this->getLocation() ) {
$this->hooks[] = $hook;
$this->uses['hooks'][] = $hook;
} else {
$this->hooks_queue[] = $hook;
$this->uses_queue['hooks'][] = $hook;
}
}
break;
Expand All @@ -118,29 +136,34 @@ public function leaveNode( \PHPParser_Node $node ) {
switch ( $node->getType() ) {
case 'Stmt_Class':
$class = end( $this->classes );
if ( ! empty( $this->method_hooks_queue ) ) {
if ( ! empty( $this->method_uses_queue ) ) {
foreach ( $class->getMethods() as $method ) {
if ( isset( $this->method_hooks_queue[ $method->getName() ] ) ) {
$method->hooks = $this->method_hooks_queue[ $method->getName() ];
if ( isset( $this->method_uses_queue[ $method->getName() ] ) ) {
$method->uses = $this->method_uses_queue[ $method->getName() ];
}
}
}

$this->method_hooks_queue = array();
$this->method_uses_queue = array();
array_pop( $this->location );
break;

case 'Stmt_Function':
end( $this->functions )->hooks = $this->hooks_queue;
$this->hooks_queue = array();
end( $this->functions )->uses = $this->uses_queue;
$this->uses_queue = array();
array_pop( $this->location );
break;

case 'Stmt_ClassMethod':
if ( ! empty( $this->hooks_queue ) ) {
$this->method_hooks_queue[ $node->name ] = $this->hooks_queue;
$this->hooks_queue = array();
/*
* Store the list of elements used by this method in the queue. We'll
* assign them to the method upon leaving the class (see above).
*/
if ( ! empty( $this->uses_queue ) ) {
$this->method_uses_queue[ $node->name ] = $this->uses_queue;
$this->uses_queue = array();
}

array_pop( $this->location );
break;
}
Expand Down
42 changes: 42 additions & 0 deletions lib/class-function-call-reflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/**
* A reflection class for a function call.
*/

namespace WP_Parser;

use phpDocumentor\Reflection\BaseReflector;
use phpDocumentor\Reflection\DocBlock\Context;

/**
* A reflection of a function call expression.
*/
class Function_Call_Reflector extends BaseReflector {

/**
* Initializes the reflector using the function statement object of
* PHP-Parser.
*
* @param \PHPParser_Node_Expr_FuncCall $node Function object
* coming from PHP-Parser.
* @param \phpDocumentor\Reflection\DocBlock\Context $context The context in
* which the node occurs.
*/
public function __construct( \PHPParser_Node_Expr $node, Context $context ) {
parent::__construct( $node, $context );
}

/**
* Returns the name for this Reflector instance.
*
* @return string
*/
public function getName() {
if (isset($this->node->namespacedName)) {
return '\\'.implode('\\', $this->node->namespacedName->parts);
}

return (string) $this->getShortName();
}
}
52 changes: 46 additions & 6 deletions lib/runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ function parse_files( $files, $root ) {
'path' => str_replace( DIRECTORY_SEPARATOR, '/', $file->getFilename() ),
);

if ( ! empty( $file->uses ) ) {
$out['uses'] = export_uses( $file->uses );
}

foreach ( $file->getIncludes() as $include ) {
$out['includes'][] = array(
'name' => $include->getName(),
Expand All @@ -56,8 +60,8 @@ function parse_files( $files, $root ) {
);
}

if ( ! empty( $file->hooks ) ) {
$out['hooks'] = export_hooks( $file->hooks );
if ( ! empty( $file->uses['hooks'] ) ) {
$out['hooks'] = export_hooks( $file->uses['hooks'] );
}

foreach ( $file->getFunctions() as $function ) {
Expand All @@ -70,8 +74,12 @@ function parse_files( $files, $root ) {
'hooks' => array(),
);

if ( ! empty( $function->hooks ) ) {
$func['hooks'] = export_hooks( $function->hooks );
if ( ! empty( $function->uses ) ) {
$func['uses'] = export_uses( $function->uses );

if ( ! empty( $function->uses['hooks'] ) ) {
$func['hooks'] = export_hooks( $function->uses['hooks'] );
}
}

$out['functions'][] = $func;
Expand Down Expand Up @@ -212,12 +220,44 @@ function export_methods( array $methods ) {
'doc' => export_docblock( $method ),
);

if ( ! empty( $method->hooks ) ) {
$meth['hooks'] = export_hooks( $method->hooks );
if ( ! empty( $method->uses ) ) {
$meth['uses'] = export_uses( $method->uses );

if ( ! empty( $method->uses['hooks'] ) ) {
$meth['hooks'] = export_hooks( $method->uses['hooks'] );
}
}

$out[] = $meth;
}

return $out;
}

/**
* Export the list of elements used by a file or structure.
*
* @param array $uses {
* @type Function_Call_Reflector[] $functions The functions called.
* }
*
* @return array
*/
function export_uses( array $uses ) {
$out = array();

// Ignore hooks here, they are exported separately.
unset( $uses['hooks'] );

foreach ( $uses as $type => $used_elements ) {
foreach ( $used_elements as $element ) {
$out[ $type ][] = array(
'name' => $element->getName(),
'line' => $element->getLineNumber(),
'end_line' => $element->getNode()->getAttribute( 'endLine' ),
);
}
}

return $out;
}

0 comments on commit 133eda2

Please sign in to comment.