diff --git a/lib/class-file-reflector.php b/lib/class-file-reflector.php index 6222dc9..3ad7182 100644 --- a/lib/class-file-reflector.php +++ b/lib/class-file-reflector.php @@ -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. @@ -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. @@ -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 ) ); @@ -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; @@ -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; } diff --git a/lib/class-function-call-reflector.php b/lib/class-function-call-reflector.php new file mode 100644 index 0000000..ccd90f4 --- /dev/null +++ b/lib/class-function-call-reflector.php @@ -0,0 +1,42 @@ +node->namespacedName)) { + return '\\'.implode('\\', $this->node->namespacedName->parts); + } + + return (string) $this->getShortName(); + } +} diff --git a/lib/runner.php b/lib/runner.php index 67f4592..40a969f 100644 --- a/lib/runner.php +++ b/lib/runner.php @@ -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(), @@ -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 ) { @@ -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; @@ -212,8 +220,12 @@ 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; @@ -221,3 +233,31 @@ function export_methods( array $methods ) { 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; +}