From b107bf87c5e492a569b75f478adcfc99a7923f9c Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:08:25 -0500 Subject: [PATCH 01/17] Some enhancements for PHP-Docblock-Generator * Add some additional options * Put on some guardrails to handle corner cases * Add exitStatus codes for CI checking * More status options less bugs * Hard tabs and lint free --- README | 14 - README.md | 58 ++ composer.json | 14 +- composer.json~ | 13 - docblock.phar.php | 6 +- src/DocBlockGenerator.class.php | 1457 ++++++++++++++++++++----------- src/docblock.php | 214 ++++- 7 files changed, 1204 insertions(+), 572 deletions(-) delete mode 100644 README create mode 100644 README.md delete mode 100644 composer.json~ diff --git a/README b/README deleted file mode 100644 index 5ab3036..0000000 --- a/README +++ /dev/null @@ -1,14 +0,0 @@ -This class will generate docblock outline for files/folders. - -Use from command line - params: -file/folder - the file or folder you want to docblock (php files) --r - to have it recursively go through a folder -target function - to docblock only a specific method/function name - -Example: -bin/docblock target.php targetFunction -or -bin/docblock target/dir -r targetFunction - -Credit to Sean Coates for the getProtos function, modified a little. -http://seancoates.com/fun-with-the-tokenizer diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae42ef4 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# PHP-Docblock-Generator + +This class will generate DocBlock comment outlines for files/folders. + +## Usage + +This class will generate docblock outline for files/folders. + +```sh +php docblock.php --source=file|folder [ --recursive ] [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] +``` + +## Params + +Use from command line - params: + + --source=path - the file or folder you want to docblock (php files) + --recursive - optional, recursively go through a folder + --exclude="p1/ p1/" - options, to exclude a series of paths + --functions="f1 f2" - optional, space delimited docblock only specific methods/functions + --dryrun - optional, test the docblock but do not make any changes + --verbose - optional, output verbose information on each file processed + +## Examples + +```sh +php docblock.php --sorce=target.php --functions="targetFunction" +php docblock.php --source=target/dir --recursive +php docblock.php --source=target/dir --recursive --dryrun +php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryrun +``` + +## TODOs: + + 1. add all proper docblock properties + 2. better checking for if docblock already exists + 3. docblocking for class properties + 4. try to gather more data for automatic insertion such as for @access + +## ChangeLog + +@author Anthony Gentile +@version 0.85 +@link http://agentile.com/docblock/ + +@version 0.86 (2014-05-19) +@link https://github.com/mbrowniebytes/PHP-Docblock-Generator + +@version 0.87 (2016-06-16) +@link https://github.com/vicebas/PHP-Docblock-Generator + +@version 0.88 (2020-12-21) +@link https://github.com/thewitness/PHP-Docblock-Generator + +## Credits + +Credit to Sean Coates for the getProtos function, modified a little. +http://seancoates.com/fun-with-the-tokenizer diff --git a/composer.json b/composer.json index 6a87d2e..cf60308 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,13 @@ { - "name": "vicebas/php-docblock-generator", + "name": "thewitness/php-docblock-generator", "description": "Generate PHPDoc Blocks in files and Folders", "require": {}, "autoload": { - "psr-0": { - "DocBlockGenerator\\": "src" - } - }, + "psr-0": { + "DocBlockGenerator\\": "src" + } + }, "bin": [ - "bin/docblock" - ] + "bin/docblock" + ] } diff --git a/composer.json~ b/composer.json~ deleted file mode 100644 index a93d5c9..0000000 --- a/composer.json~ +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "agentile/php-docblock-generator", - "description": "Generate PHPDoc Blocks in files and Folders", - "require": {}, - "autoload": { - "psr-0": { - "DocBlockGenerator\\": "src" - } - }, - "bin": [ - "bin/docblock" - ], -} diff --git a/docblock.phar.php b/docblock.phar.php index a6847b3..3722e76 100644 --- a/docblock.phar.php +++ b/docblock.phar.php @@ -1,9 +1,9 @@ addFile('src/docblock.php'); $app->addFile('src/DocBlockGenerator.class.php'); -$defaultStub = $app->createDefaultStub("src/docblock.php"); -$stub = "#!/usr/bin/env php \n".$defaultStub; +$defaultStub = $app->createDefaultStub('src/docblock.php'); +$stub = "#!/usr/bin/env php \n".$defaultStub; $app->setStub($stub); $app->stopBuffering(); ?> diff --git a/src/DocBlockGenerator.class.php b/src/DocBlockGenerator.class.php index 38b2f52..dde0e7a 100644 --- a/src/DocBlockGenerator.class.php +++ b/src/DocBlockGenerator.class.php @@ -2,468 +2,860 @@ namespace DocBlockGenerator; class DocBlockGenerator { + public $exts = array('.php', '.php4', '.php5', '.phps', '.inc'); + + public $description_placeholder = 'Insert description here'; // text, if any, to add as placeholder for description + + public $target; // the starting point to parse + public $functions = array(); // array of excluded functions + public $exclude = array(); // array of excluded paths + public $recursive = false; // recurse through all paths + public $dryrun = false; // dryrun, don't make changes + public $verbose = false; // print verbose output + public $anonymous = false; // true, add docblock for anonymous functions; false, skip + public $full = false; // true, add all docblock params; false, add minimal + + public $file_contents; + public $log = array(); + + private $version = '0.88'; + + private $nbr_docblocks = 0; + private $total_files = 0; + private $excluded_files = 0; + + private $total_functions = 0; + private $converted_functions = 0; + private $skipped_functions = 0; + + private $total_classes = 0; + private $converted_classes = 0; + private $skipped_classes = 0; + private $nondb_classes = array(); + private $db_classes = array(); + + private $exist_db_comments = 0; + private $exist_nondb_comments = 0; + private $nondb_functions = array(); + private $db_functions = array(); + + private $skipped_files = 0; + + private $errors = 0; + + /** + * __construct + * + * @param $target + * @param $functions + * @param $recursive + * @param $exclude + * @param mixed $verbose + * @param mixed $dryrun + * + * @return void + * + * @access public + * @static + * @since 0.85 + */ + public function __construct($target = null, $functions = null, $recursive = false, $exclude = null, + $verbose = false, $dryrun = false, $anonymous = false, $full = false) { + $this->target = $target; + $this->recursive = $recursive; + $this->verbose = $verbose; + $this->dryrun = $dryrun; + + if ($functions !== null && $functions != '') { + $this->functions = explode(' ', $functions); + } else { + $this->functions = null; + } + + if ($exclude !== null && $exclude != '') { + $this->exclude = explode(' ', $exclude); + } else { + $this->exclude = null; + } + } + + /** + * result + * Print output to command line + * + * + * @return string + * + * @access public + * @static + * @since 0.85 + */ + public function result() { + if ($this->verbose) { + $str = ''; + + foreach ($this->log as $log_item) { + $str .= sprintf('| File: %-84s|' . PHP_EOL, $log_item); + } + + print $str; + } + } + + /** + * summary + * Print summary output to command line + * + * + * @return null + * + * @access public + * @static + * @since 0.87 + * @param mixed $end + * @param mixed $start + */ + public function summary($end, $start) { + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + + printf('| %-90s|' . PHP_EOL, sprintf('DocBlock Generation took %.2f seconds.', $end - $start)); + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('| %-90s|' . PHP_EOL, sprintf('There were %d files total scanned and %d files excluded.', + $this->total_files, $this->excluded_files)); + + printf('| %-90s|' . PHP_EOL, sprintf('There were %d functions found and %d %s converted.', + $this->total_functions, $this->converted_functions, ($this->dryrun ? 'would be':'were'))); + + printf('| %-90s|' . PHP_EOL, sprintf('There were %d classes found and %d %s converted.', + $this->total_classes, $this->converted_classes, ($this->dryrun ? 'would be':'were'))); + + printf('| %-90s|' . PHP_EOL, sprintf('There were %d existing docblock functions found.', + $this->exist_db_comments)); + + printf('| %-90s|' . PHP_EOL, sprintf('WARNING: There were %d existing non-docblock functions found.', + $this->exist_nondb_comments)); + + if ($this->dryrun) { + printf('| %-90s|' . PHP_EOL, sprintf('There would be %d read/write errors expected.', $this->errors)); + } else { + printf('| %-90s|' . PHP_EOL, sprintf('There were %d read/write errors.', $this->errors)); + } + + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL . PHP_EOL); + } + + /** + * preface + * Print details about what is going to happen + * + * + * @return null + * + * @access public + * @static + * @since 0.87 + * @param mixed $end + * @param mixed $start + */ + public function preface() { + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('| %-90s|' . PHP_EOL, sprintf('PHP DocBlock Generator - Starting %s', ($this->dryrun ? '- Dry Run Only':''))); + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('| %-90s|' . PHP_EOL, sprintf('Processing Starting - Settings below'),); + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('| %-90s|' . PHP_EOL, sprintf('Target Path: %s', $this->target)); + printf('| %-90s|' . PHP_EOL, sprintf('Recursion is: %s', $this->recursive ? 'Enabled':'Disabled')); + printf('| %-90s|' . PHP_EOL, sprintf('File Exclusions: %s', ($this->exclude ? implode(', ', $this->exclude) : 'None Excluded'))); + printf('| %-90s|' . PHP_EOL, sprintf('Included Functions: %s', ($this->functions ? implode(', ', $this->functions) : 'All Functions'))); + printf('| %-90s|' . PHP_EOL, sprintf('Anonymous Functions: %s', ($this->anonymous ? 'Included' : 'Skipped'))); + printf('| %-90s|' . PHP_EOL, sprintf('PHPDoc Comment Style: %s', ($this->full ? 'Full' : 'Short'))); + + if ($this->verbose) { + printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + } + } + + /** + * exitCode + * Return the exit code + * + * + * @return int + * + * @access public + * @static + * @since 0.87 + */ + public function exitCode() { + if ($this->errors) { + if ($this->dryrun) { + return 3; + } else { + return 2; + } + } else { + if ($this->exist_nondb_comments || $this->exist_nondb_classes) { + return 1; + } else { + return 0; + } + } + } + + /** + * start + * Begin the docblocking process, determine if a file or folder was given + * + * @return void + * + * @access public + * @static + * @since 0.85 + */ + public function start() { + if (is_file($this->target)) { + $valid_file = $this->fileCheck($this->target); + + if ($valid_file == false) { + return false; + } + + if (!$this->excludeCheck($this->target)) { + $this->fileDocBlock(); + } + } elseif (is_dir($this->target)) { + if ($this->recursive == true) { + $files = $this->scanDirectories($this->target, true); + } else { + $files = $this->scanDirectories($this->target); + } + + foreach ($files as $file) { + if (!$this->excludeCheck($file)) { + $this->target = $file; + $this->fileDocBlock(); + } + } + } else { + $this->log[] = 'This is not a file or folder.'; + + return false; + } + } + + /** + * excludeCheck + * See if a file is excluded from processing + * + * @param $target + * + * @return bool + * + * @access public + * @static + * @since 0.85 + */ + public function excludeCheck($target) { + $this->total_files++; + + if (is_array($this->exclude) && sizeof($this->exclude)) { + foreach ($this->exclude as $path) { + if (strpos($target, $path) !== false) { + $this->log[] = "{$target} - is Excluded."; + $this->excluded_files++; + + return true; + } + } + } + + return false; + } + + /** + * functionCheck + * See if a function is included for processing + * + * @param $target_func + * + * @return bool + * + * @access public + * @static + * @since 0.89 + */ + public function functionCheck($target_func) { + if (is_array($this->functions) && sizeof($this->functions)) { + foreach ($this->functions as $func) { + if ($target_func === $func) { + return true; + } + } + + return false; + } + + return true; + } + + /** + * fileCheck + * Make sure we can deal with the target file + * + * @param $target + * + * @return bool + * + * @access public + * @static + * @since 0.85 + */ + public function fileCheck($target) { + $file_ext = strtolower(substr($target, strrpos($target, '.'))); + $bool = true; + + if (!in_array($file_ext, $this->exts, true)) { + $this->log[] = "{$target} is not a PHP file."; + $bool = false; + } + + if (!is_readable($target)) { + $this->errors++; + $this->log[] = "{$target} is not readable."; + $bool = false; + } + + if (!is_writable($target)) { + $this->errors++; + $this->log[] = "{$target} is not writeable.\nCheck file permissions"; + $bool = false; + } + + return $bool; + } + + /** + * fileDocBlock + * Shell method for docblock operations, explodes file, performs docblock methods, impodes. + * + * @return void + * + * @access public + * @static + * @since 0.85 + */ + public function fileDocBlock() { + $this->nbr_docblocks = 0; + $this->file_contents = file_get_contents($this->target); + + list($funcs, $classes) = $this->getProtos(); + + $handle = fopen($this->target, 'r'); + + if ($contents = fread($handle, filesize($this->target))) { + $contents = explode(PHP_EOL, $contents); + $contents = $this->docBlock($contents, $funcs, $classes, $this->functions); + $contents = implode(PHP_EOL, $contents); + fclose($handle); + + if ($this->nbr_docblocks == 0) { + $this->log[] = "{$this->target} - Nothing to DocBlock"; + $this->skipped_files++; + } else { + if (!$this->dryrun) { + $handle = fopen($this->target, 'w'); + + if (fwrite($handle, $contents)) { + $this->converted_functions += $this->nbr_docblocks; + + $this->log[] = "{$this->target} - DocBlocked " . $this->nbr_docblocks; + fclose($handle); + + return; + } else { + fclose($handle); + $this->log[] = "WARNING: Could not write new content. - Check Permissions"; + $this->errors++; + + return; + } + } else { + if (is_writable($this->target)) { + $this->converted_functions += $this->nbr_docblocks; + } else { + $this->errors++; + } + } + } + } else { + fclose($handle); + $this->log[] = "Could not get file contents.\nCheck Permissions"; + $this->errors++; + + return; + } + } + + /** + * getProtos + * This function goes through the tokens to gather the arrays of information we need + * + * @return array + * + * @access public + * @static + * @since 0.85 + */ + public function getProtos() { + $tokens = token_get_all($this->file_contents); + $funcs = array(); + $classes = array(); + $curr_class = ''; + $curr_func = ''; + $class_depth = 0; + $count = count($tokens); + $comment = false; + $dcomment = false; + + for ($i = 0; $i < $count; $i++) { + if (is_array($tokens[$i]) && $tokens[$i][0] == T_COMMENT) { + $comment = true; + $dcomment = false; + } + + if (is_array($tokens[$i]) && $tokens[$i][0] == T_DOC_COMMENT) { + $dcomment = true; + $comment = false; + } - public $exts = array('.php', '.php4', '.php5', '.phps', '.inc'); - public $target; - public $target_function; - public $recursive; - public $file_contents; - public $log = array(); - - - /** - * __construct - * - * @param $target - * @param $target_function - * @param $recursive - * - * @return void - * - * @access public - * @static - * @since 0.85 - */ - public function __construct($target, $target_function = null, $recursive = false) - { - $this->target = $target; - $this->target_function = $target_function; - $this->recursive = $recursive; - } - - /** - * result - * Print output to command line - * - * - * @return string - * - * @access public - * @static - * @since 0.85 - */ - public function result() - { - $str = ''; - foreach ($this->log as $log_item) { - $str .= "{$log_item}\n"; - } - echo $str; - } - - /** - * start - * Begin the docblocking process, determine if a file or folder was given - * - * @return void - * - * @access public - * @static - * @since 0.85 - */ - public function start() - { - if (is_file($this->target)) { - $valid_file = $this->fileCheck($this->target); - if ($valid_file == false) { - return false; - } - $this->fileDocBlock(); - } elseif (is_dir($this->target)) { - if ($this->recursive == true) { - $files = $this->scanDirectories($this->target, true); - } else { - $files = $this->scanDirectories($this->target); - } - foreach ($files as $file) { - $this->target = $file; - $this->fileDocBlock(); - } - } else { - $this->log[] = 'This is not a file or folder.'; - return false; - } - } - - /** - * fileCheck - * Make sure we can deal with the target file - * - * @param $target - * - * @return bool - * - * @access public - * @static - * @since 0.85 - */ - public function fileCheck($target) - { - $file_ext = strtolower(substr($target, strrpos($target, '.'))); - $bool = true; - if (!in_array($file_ext, $this->exts)) { - $this->log[] = "{$target} is not a PHP file."; - $bool = false; - } - if (!is_readable($target)) { - $this->log[] = "{$target} is not readable."; - $bool = false; - } - if (!is_writable($target)) { - $this->log[] = "{$target} is not writeable.\nCheck file permissions"; - $bool = false; - } - return $bool; - } - - /** - * fileDocBlock - * Shell method for docblock operations, explodes file, performs docblock methods, impodes. - * - * @return void - * - * @access public - * @static - * @since 0.85 - */ - public function fileDocBlock() - { - $this->file_contents = file_get_contents($this->target); - list($funcs, $classes) = $this->getProtos(); - $handle = fopen($this->target, 'r'); - if ($contents = fread($handle, filesize($this->target))) { - $contents = explode("\n", $contents); - $contents = $this->docBlock($contents, $funcs, $classes, $this->target_function); - $contents = implode("\n", $contents); - fclose($handle); - $handle = fopen($this->target, 'w'); - if (fwrite($handle, $contents)) { - $this->log[] = "{$this->target} Doc Blocked!"; - fclose($handle); - return; - } else { - fclose($handle); - $this->log[] = "Could not write new content.\nCheck Permissions"; - return; - } - } else { - fclose($handle); - $this->log[] = "Could not get file contents.\nCheck Permissions"; - return; - } - } - - /** - * getProtos - * This function goes through the tokens to gather the arrays of information we need - * - * @return array - * - * @access public - * @static - * @since 0.85 - */ - public function getProtos() - { - $tokens = token_get_all($this->file_contents); - $funcs = array(); - $classes = array(); - $curr_class = ''; - $curr_func = ''; - $class_depth = 0; - $count = count($tokens); - for ($i = 0; $i < $count; $i++) { if (is_array($tokens[$i]) && $tokens[$i][0] == T_RETURN) { - $funcs[$curr_func]['return'] = 'returns'; + if ($curr_func != '' && isset($funcs[$curr_func])) { + $funcs[$curr_func]['return'] = 'returns'; + } + } + + if (is_array($tokens[$i]) && $tokens[$i][0] == T_CLASS) { + /* collect some statistics */ + $this->total_classes++; + + $line = $tokens[$i][2]; + ++$i; // whitespace; + $curr_class = $tokens[++$i][1]; + + if ($comment) { + $this->exist_nondb_comments++; + + $comment = false; + $dcomment = false; + + $this->nondb_classes[$curr_class] = $curr_class; + } elseif ($dcomment) { + $this->exist_db_comments++; + + $comment = false; + $dcomment = false; + + $this->db_classes[$curr_class] = $curr_class; + } + + if (!in_array(array('line' => $line, 'name' => $curr_class), $classes, true)) { + $classes[] = array( + 'line' => $line, + 'name' => $curr_class + ); + } + + while (true) { + $i++; + + if ($i >= $count) { + break; + } elseif (isset($tokens[$i])) { + if ($tokens[$i] == '{') { + break; + } + } + } + + ++$i; + $class_depth = 1; + + continue; } - if (is_array($tokens[$i]) && $tokens[$i][0] == T_CLASS) { - $line = $tokens[$i][2]; - ++$i; // whitespace; - $curr_class = $tokens[++$i][1]; - if (!in_array(array('line' => $line, 'name' => $curr_class), $classes)) { - $classes[] = array('line' => $line, 'name' => $curr_class); - } - while ($tokens[++$i] != '{') {} - ++$i; - $class_depth = 1; - continue; - } elseif (is_array($tokens[$i]) && $tokens[$i][0] == T_FUNCTION) { - $next_by_ref = FALSE; - $this_func = array(); + + if (is_array($tokens[$i]) && $tokens[$i][0] == T_FUNCTION) { + /* collect some statistics */ + $this->total_functions++; + + $next_by_ref = false; + $this_func = array(); $func_status = array(); + $curr_func = $tokens[$i][1]; + + if ($comment) { + $this->exist_nondb_comments++; + + $comment = false; + $dcomment = false; + + $this->nondb_functions[$curr_func] = $curr_func; + } elseif ($dcomment) { + $this->exist_db_comments++; + + $comment = false; + $dcomment = false; + + $this->db_functions[$curr_func] = $curr_func; + } - if ($tokens[$i-2][1] == 'static') { + if (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == 'static') { $func_status['static'] = true; } else { $func_status['static'] = false; } - if ($tokens[$i-2][1] != 'static') { - if ($tokens[$i-2][1] == 'public' || $tokens[$i-2][1] == 'private'|| $tokens[$i-2][1] == 'protected') { - $func_status['access'] = $tokens[$i-2][1]; + if (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] != 'static') { + if ($tokens[$i - 2][1] == 'public' || $tokens[$i - 2][1] == 'private' || $tokens[$i - 2][1] == 'protected') { + $func_status['access'] = $tokens[$i - 2][1]; } } - if ($tokens[$i-2][1] == 'static') { - if ($tokens[$i-4][1] == 'public' || $tokens[$i-4][1] == 'private'|| $tokens[$i-4][1] == 'protected') { - $func_status['access'] = $tokens[$i-4][1]; + + if (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == 'static') { + if (isset($tokens[$i - 4][1])) { + if ($tokens[$i - 4][1] == 'public' || $tokens[$i - 4][1] == 'private' || $tokens[$i - 4][1] == 'protected') { + $func_status['access'] = $tokens[$i - 4][1]; + } } - } - while ($tokens[++$i] != '{') { - if (is_array($tokens[$i]) && $tokens[$i][0] != T_WHITESPACE) { - if (!$this_func) { + // Proceed through the function till the first bracket + while (true) { + $i++; + + if ($i >= $count) { + break; + } elseif (isset($tokens[$i])) { + if ($tokens[$i] == '{') { + break; + } + } + + if (is_array($tokens[$i]) && $tokens[$i][0] != T_WHITESPACE) { + if (!sizeof($this_func)) { $curr_func = $tokens[$i][1]; - $this_func = array( - 'name' => $tokens[$i][1], - 'class' => $curr_class, - 'line' => $tokens[$i][2], - ); - } elseif ($tokens[$i][0] == T_VARIABLE) { - $this_func['params'][] = array( - 'byRef' => $next_by_ref, - 'name' => $tokens[$i][1], - ); - $next_by_ref = FALSE; - } - } elseif ($tokens[$i] == '&') { - $next_by_ref = TRUE; - } elseif ($tokens[$i] == '=') { - while (!in_array($tokens[++$i], array(')', ','))) { - if ($tokens[$i][0] != T_WHITESPACE) { - break; - } - } - $this_func['params'][count($this_func['params']) - 1]['default'] = $tokens[$i][1]; - } - } - - $funcs[$curr_func] = $this_func + $func_status; - } elseif ($tokens[$i] == '{' || $tokens[$i] == 'T_CURLY_OPEN' || $tokens[$i] == 'T_DOLLAR_OPEN_CURLY_BRACES') { - ++$class_depth; - } elseif ($tokens[$i] == '}') { - --$class_depth; - } - - if ($class_depth == 0) { - $curr_class = ''; - } - } - - return array($funcs, $classes); - } - - /** - * docBlock - * Main docblock function, determines if class or function docblocking is need and calls - * appropriate subfunction. - * - * @param $arr - * @param $funcs - * @param $classes - * @param $target_function - * - * @return array - * - * @access public - * @static - * @since 0.85 - */ - public function docBlock($arr, $funcs, $classes, $target_function) - { - $func_lines = array(); - foreach ($funcs as $func) { - $func_lines[] = $func['line']; - } - $class_lines = array(); - foreach ($classes as $class) { - $class_lines[] = $class['line']; - } - $class_or_func = ''; - $count = count($arr); - for($i = 0; $i < $count; $i++) { - $line = $i + 1; - $code = $arr[$i]; - - if (in_array($line, $class_lines) && !$this->docBlockExists($arr[($i - 1)])) { - $class_or_func = 'class'; - } elseif (in_array($line, $func_lines) && !$this->docBlockExists($arr[($i - 1)])) { - $class_or_func = 'func'; - } else { - continue; - } - - if ($class_or_func === 'func') { - $data = $this->getData($line, $funcs); - } elseif ($class_or_func === 'class') { - $data = $this->getData($line, $classes); - } - if ($target_function !== null && $target_function !== '') { - if ($data['name'] !== $target_function) { - continue; - } - } - $indent_count = $this->getStrIndent($code); - $indent = ''; - for($indt = 0; $indt < $indent_count; $indt++) { - $indent .= ' '; - } - if ($class_or_func === 'func') { - $doc_block = $this->functionDocBlock($indent, $data); - } elseif ($class_or_func === 'class') { - $doc_block = $this->classDocBlock($indent, $data); - } - $arr[$i] = $doc_block . $arr[$i]; - } - return $arr; - } - - /** - * scanDirectories - * Get all specific files from a directory and if recursive, subdirectories - * - * @param $dir - * @param $recursive - * @param $data - * - * @return array - * - * @access public - * @static - * @since 0.85 - */ - public function scanDirectories($dir, $recursive = false, $data = array()) - { - // set filenames invisible if you want - $invisible = array('.', '..', '.htaccess', '.htpasswd'); - // run through content of root directory - $dir_content = scandir($dir); - foreach ($dir_content as $key => $content) { - // filter all files not accessible - $path = $dir . '/' . $content; - if (!in_array($content, $invisible)) { - // if content is file & readable, add to array - if (is_file($path) && is_readable($path)) { - // what is the ext of this file - $file_ext = strtolower(substr($path, strrpos($path, "."))); - // if this file ext matches the ones from our array - if (in_array($file_ext, $this->exts)) { - // save file name with path - $data[] = $path; - } - // if content is a directory and readable, add path and name - } elseif (is_dir($path) && is_readable($path)) { - // recursive callback to open new directory - if ($recursive == true) { - $data = $this->scanDirectories($path, true, $data); - } - } - } - } - return $data; - } - - /** - * getData - * Retrieve method or class information from our arrays - * - * @param $line - * @param $arr - * - * @return mixed - * - * @access public - * @static - * @since 0.85 - */ - public function getData($line, $arr) - { - foreach ($arr as $k => $v) { - if ($line == $v['line']) { - return $arr[$k]; - } - } - return false; - } - - /** - * docBlockExists - * Primitive check to see if docblock already exists - * - * @param $line - * - * @return bool - * - * @access public - * @static - * @since 0.85 - */ - public function docBlockExists($line) - { - // ok we are simply going to check the line above the function and look for */ - // TODO: make this a more accurate check. - $indent = $this->getStrIndent($line); - if ($indent > 0) { - $line = substr($line, ($indent - 1)); - } - $len = strlen($line); - if ($len == 0) { - return false; - } - $asterik = false; - for ($i = 0; $i < $len; $i++) { - if ($line[$i] == '*') { - $asterik = true; - } elseif ($line[$i] == '/' && $asterik == true) { - return true; - } else { - $asterik = false; - } - } - return false; - } - - /** - * functionDocBlock - * Docblock for function - * - * @param $indent - * @param $data - * - * @return string - * - * @access public - * @static - * @since 0.85 - */ - public function functionDocBlock($indent, $data) - { - $doc_block = "{$indent}/**\n"; - $doc_block .= "{$indent} * {$data['name']}\n"; - $doc_block .= "{$indent} * Insert description here\n"; - $doc_block .= "{$indent} *\n"; - if (isset($data['params'])) { - foreach($data['params'] as $func_param) { - $doc_block .= "{$indent} * @param ". (isset($func_param['default'])?$this->decodeType($func_param['default']):'type') . " {$func_param['name']}\n"; - } - $doc_block .= "{$indent} *\n"; - } + + if ($curr_func == '') { + // no name + $this->skipped_functions++; + continue; + } + + if (substr($tokens[$i][1], 0, 1) == '$') { + // anonymous function + if (!$this->anonymous) { + continue; + } else { + // go back and try to find php function being used in + for ($j = $i; $i - $j < 10; $j--) { + if (is_array($tokens[$j]) && $tokens[$j][0] == T_STRING) { + $tokens[$i][1] = $tokens[$j][1]; + + break; + } + } + } + } + + $this_func = array( + 'name' => $tokens[$i][1], + 'class' => $curr_class, + 'line' => $tokens[$i][2], + ); + } elseif ($tokens[$i][0] == T_VARIABLE) { + if ($this_func) { + $this_func['params'][] = array( + 'byRef' => $next_by_ref, + 'name' => $tokens[$i][1], + ); + + $next_by_ref = false; + } + } + } elseif ($tokens[$i] == '&') { + $next_by_ref = true; + } elseif ($tokens[$i] == '=') { + while (!in_array($tokens[++$i], array(')', ','), true)) { + // default may be a negative (-) number + if ($tokens[$i][0] != T_WHITESPACE && $tokens[$i][0] != '-') { + break; + } + } + + if ($this_func) { + if (isset($tokens[$i][1])) { + $this_func['params'][count($this_func['params']) - 1]['default'] = $tokens[$i][1]; + } + } + } + } + + if ($this_func) { + $funcs[$curr_func] = $this_func + $func_status; + } else { + $this->skipped_functions++; + } + + $comment = false; + $dcomment = false; + } elseif ($tokens[$i] == '{' || $tokens[$i] == 'T_CURLY_OPEN' || $tokens[$i] == 'T_DOLLAR_OPEN_CURLY_BRACES') { + ++$class_depth; + } elseif ($tokens[$i] == '}') { + --$class_depth; + } + + if ($class_depth == 0) { + $curr_class = ''; + } + } + + return array($funcs, $classes); + } + + /** + * docBlock + * Main docblock function, determines if class or function docblocking is need and calls + * appropriate subfunction. + * + * @param $arr + * @param $funcs + * @param $classes + * @param $functions + * + * @return array + * + * @access public + * @static + * @since 0.85 + */ + public function docBlock($arr, $funcs, $classes, $functions) { + $func_lines = array(); + + foreach ($funcs as $index => $func) { + $func_lines[] = $func['line']; + } + + $class_lines = array(); + + foreach ($classes as $class) { + $class_lines[] = $class['line']; + } + + $class_or_func = ''; + + $count = count($arr); + + for ($i = 0; $i < $count; $i++) { + $line = $i + 1; + $code = $arr[$i]; + + if (in_array($line, $class_lines, true) && !$this->docBlockExists($arr[($i - 1)])) { + $class_or_func = 'class'; + } elseif (in_array($line, $func_lines, true) && !$this->docBlockExists($arr[($i - 1)])) { + $class_or_func = 'func'; + } else { + continue; + } + + if ($class_or_func === 'func') { + $data = $this->getData($line, $funcs); + } elseif ($class_or_func === 'class') { + $data = $this->getData($line, $classes); + } + + if (!$this->functionCheck($data['name'])) { + $this->skipped_functions++; + continue; + } + + $indent = $this->getStrIndent($code); + + if ($class_or_func === 'func') { + $doc_block = $this->functionDocBlock($indent, $data); + } elseif ($class_or_func === 'class') { + $doc_block = $this->classDocBlock($indent, $data); + } + + $this->nbr_docblocks++; + + $arr[$i] = $doc_block . $arr[$i]; + } + + return $arr; + } + + /** + * scanDirectories + * Get all specific files from a directory and if recursive, subdirectories + * + * @param $dir + * @param $recursive + * @param $data + * + * @return array + * + * @access public + * @static + * @since 0.85 + */ + public function scanDirectories($dir, $recursive = false, $data = array()) { + // set filenames invisible if you want + $invisible = array('.', '..', '.htaccess', '.htpasswd'); + // run through content of root directory + $dir_content = scandir($dir); + + foreach ($dir_content as $key => $content) { + // filter all files not accessible + $path = $dir . '/' . $content; + + if (!in_array($content, $invisible, true)) { + // if content is file & readable, add to array + if (is_file($path) && is_readable($path)) { + // what is the ext of this file + $file_ext = strtolower(substr($path, strrpos($path, '.'))); + // if this file ext matches the ones from our array + if (in_array($file_ext, $this->exts, true)) { + // save file name with path + $data[] = $path; + } + // if content is a directory and readable, add path and name + } elseif (is_dir($path) && is_readable($path)) { + // recursive callback to open new directory + if ($recursive == true) { + $data = $this->scanDirectories($path, true, $data); + } + } + } + } + + return $data; + } + + /** + * getData + * Retrieve method or class information from our arrays + * + * @param $line + * @param $arr + * + * @return mixed + * + * @access public + * @static + * @since 0.85 + */ + public function getData($line, $arr) { + foreach ($arr as $k => $v) { + if ($line == $v['line']) { + return $arr[$k]; + } + } + + return false; + } + + /** + * docBlockExists + * Primitive check to see if docblock already exists + * + * @param $line + * + * @return bool + * + * @access public + * @static + * @since 0.85 + */ + public function docBlockExists($line) { + // ok we are simply going to check the line above the function and look for */ + // TODO: make this a more accurate check. + $line = ltrim($line); + $len = strlen($line); + + if ($len == 0) { + return false; + } + + $asterik = false; + + for ($i = 0; $i < $len; $i++) { + if ($line[$i] == '*') { + $asterik = true; + } elseif ($line[$i] == '/' && $asterik == true) { + return true; + } else { + $asterik = false; + } + } + + return false; + } + + /** + * functionDocBlock + * Docblock for function + * + * @param $indent + * @param $data + * + * @return string + * + * @access public + * @static + * @since 0.85 + */ + public function functionDocBlock($indent, $data) { + $doc_block = $indent.'/**' . PHP_EOL; + $doc_block .= $indent." * {$data['name']}".PHP_EOL; + + if (!empty($this->description_placeholder)) { + $doc_block .= $indent . ' * ' . $this->description_placeholder . PHP_EOL; + } + + $doc_block .= $indent . ' *' . PHP_EOL; + $doc_block .= $indent . ' *' . PHP_EOL; + + if (isset($data['params'])) { + foreach ($data['params'] as $func_param) { + $doc_block .= $indent." * @param ". (isset($func_param['default'])?$this->decodeType($func_param['default']):'type') . " {$func_param['name']}" . PHP_EOL; + } + + $doc_block .= $indent . ' *' . PHP_EOL; + } + if (isset($data['return'])) { - $doc_block .= "{$indent} * @return type\n"; + $doc_block .= $indent . ' * @return type' . PHP_EOL; } + if (!empty($data['access'])) { - $doc_block .= "{$indent} * @access {$data['access']}\n"; + $doc_block .= $indent . ' * @access ' . $data['access'] . PHP_EOL; } + if ($data['static']) { - $doc_block .= "{$indent} * @static\n"; + $doc_block .= $indent . ' * @static' . PHP_EOL; + } + + if ($this->full) { + $doc_block .= $indent . ' *' . PHP_EOL; + $doc_block .= $indent . ' * @access' . PHP_EOL; + $doc_block .= $indent . ' * @static' . PHP_EOL; + $doc_block .= $indent . ' * @see' . PHP_EOL; + $doc_block .= $indent . ' * @since' . PHP_EOL; } - // We can't determine these automatically yet. - //$doc_block .= "{$indent} * @see\n"; - //$doc_block .= "{$indent} * @since\n"; - $doc_block .= "{$indent} */\n"; - return $doc_block; - } + $doc_block .= $indent.' */'.PHP_EOL; + + return $doc_block; + } /** * Decode the parameter type @@ -488,63 +880,140 @@ public function decodeType($type) { if ($type === true) { $typeToReturn = 'bool'; } + return $typeToReturn; } - /** - * classDocBlock - * Docblock for class - * - * @param $indent - * @param $data - * - * @return string - * - * @access public - * @static - * @since 0.85 - */ - public function classDocBlock($indent, $data) - { - $doc_block = "{$indent}/**\n"; - $doc_block .= "{$indent} * {$data['name']}\n"; - $doc_block .= "{$indent} * Insert description here\n"; - $doc_block .= "{$indent} *\n"; - //$doc_block .= "{$indent} * @category\n"; - //$doc_block .= "{$indent} * @package\n"; - //$doc_block .= "{$indent} * @author\n"; - //$doc_block .= "{$indent} * @copyright\n"; - //$doc_block .= "{$indent} * @license\n"; - //$doc_block .= "{$indent} * @version\n"; - //$doc_block .= "{$indent} * @link\n"; - //$doc_block .= "{$indent} * @see\n"; - //$doc_block .= "{$indent} * @since\n"; - $doc_block .= "{$indent} */\n"; - - return $doc_block; - } - - /** - * getStrIndent - * Returns indentation count of a string - * - * @param $str - * @param $count - * - * @return int - * - * @access public - * @static - * @since 0.85 - */ - public function getStrIndent($str, $count = 0) - { - if (isset($str[0])) { - return ($str[0] == ' ') ? $this->getStrIndent(substr($str, 1), ($count + 1)) : $count; - } else { - return $count; - } - } + /** + * classDocBlock + * Docblock for class + * + * @param $indent + * @param $data + * + * @return string + * + * @access public + * @static + * @since 0.85 + */ + public function classDocBlock($indent, $data) { + $doc_block = $indent . '/**' . PHP_EOL; + $doc_block .= $indent . " * {$data['name']}" . PHP_EOL; + + if (!empty($this->description_placeholder)) { + $doc_block .= $indent . ' * ' . $this->description_placeholder . PHP_EOL; + } + + $doc_block .= $indent . ' *' . PHP_EOL; + $doc_block .= $indent . ' *' . PHP_EOL; + + if ($this->full) { + $doc_block .= $indent . ' * @category' . PHP_EOL; + $doc_block .= $indent . ' * @package' . PHP_EOL; + $doc_block .= $indent . ' * @author' . PHP_EOL; + $doc_block .= $indent . ' * @copyright' . PHP_EOL; + $doc_block .= $indent . ' * @license' . PHP_EOL; + $doc_block .= $indent . ' * @version' . PHP_EOL; + $doc_block .= $indent . ' * @link' . PHP_EOL; + $doc_block .= $indent . ' * @see' . PHP_EOL; + $doc_block .= $indent . ' * @since' . PHP_EOL; + } + + $doc_block .= $indent . ' */' . PHP_EOL; + + return $doc_block; + } + + /** + * getStrIndent + * Returns indentation of a string + * + * @param $str + * @param $count + * + * @return int + * + * @access public + * @static + * @since 0.86 + */ + public function getStrIndent($str) { + // preserve tabs and spaces + if (preg_match('/^(\s+)/', $str, $matches)) { + $indent = $matches[1]; + } else { + $indent = ''; + } + + return $indent; + } + /** + * getVersion + * Returns the current version + * + * @return string + * + * @access public + * @static + * @since 0.87 + */ + public function getVersion() { + return 'PHP DocBlock Generator, Version ' . $this->version . PHP_EOL; + } + + /** + * getHelp + * Returns the current usage requirements + * + * @return string + * + * @access public + * @static + * @since 0.87 + */ + public function getHelp() { + $output = PHP_EOL . 'usage: docblock.php [ -s | --source=S ] [ -r | --recursive ]' . PHP_EOL; + $output .= ' [ -x | --exclude=S ] [ -f | --functions=S ] [ --dryrun ] [ --verbose ]' . PHP_EOL . PHP_EOL; + $output .= ' [ --anonymous ] [ --full ]' . PHP_EOL; + $output .= 'Utility for inserting DocBlock into PHP code' . PHP_EOL . PHP_EOL; + + $output .= 'Options:' . PHP_EOL; + $output .= ' -s | --source Process the source directory here' . PHP_EOL; + $output .= ' -r | --recursive Recursively process files in directory' . PHP_EOL; + $output .= ' -x | --exclude Space delimited string of paths to exclude' . PHP_EOL; + $output .= ' -f | --functions A space delimited set of functions to document' . PHP_EOL; + $output .= ' --anonymous Include Anonymous PHP function comments' . PHP_EOL; + $output .= ' --full Use the complete PHP DocBlock comment format' . PHP_EOL; + $output .= ' --dryrun Generate a log. Do not update files' . PHP_EOL; + $output .= ' --verbose Print the entire log, not just summaries' . PHP_EOL . PHP_EOL; + + $output .= 'Returns:' . PHP_EOL; + $output .= ' 0 - No errors encountered, all functions correctly documented' . PHP_EOL; + $output .= ' 1 - Some functions remain undocumented, or in non-DocBlock format' . PHP_EOL; + $output .= ' 2 - General read/write error encountered' . PHP_EOL; + $output .= ' 3 - Read write errors would encounted in dryrun' . PHP_EOL . PHP_EOL; + + $output .= 'This utility will traverse a source directory for PHP files and insert' . PHP_EOL; + $output .= 'DocBlock code for all functions and classes. By default, it will modify' . PHP_EOL; + $output .= 'files in place, but you may also run a --dryrun to check for undocumented' . PHP_EOL; + $output .= 'functions.' . PHP_EOL . PHP_EOL; + + $output .= 'You may exclude files that match a pattern, for example the' . PHP_EOL; + $output .= 'following is a valid --exclude parameter:' . PHP_EOL . PHP_EOL; + $output .= ' --exclude="vendor/ tools/"' . PHP_EOL . PHP_EOL; + + $output .= 'You may specify specific functions by name, for example the' . PHP_EOL; + $output .= 'following is a valid --functions parameter:' . PHP_EOL . PHP_EOL; + $output .= ' --functions="f1 f2"' . PHP_EOL . PHP_EOL; + + $output .= 'That exclude option will exclude directories named vendor and tools' . PHP_EOL; + $output .= 'from being scanned and altered.' . PHP_EOL . PHP_EOL; + + $output .= 'NOTE: Legacy options processing has been deprecated and removed' . PHP_EOL; + $output .= 'from this release.' . PHP_EOL . PHP_EOL; + + return $output; + } } -?> diff --git a/src/docblock.php b/src/docblock.php index 8cde822..474cd2c 100644 --- a/src/docblock.php +++ b/src/docblock.php @@ -4,20 +4,26 @@ * * This class will generate docblock outline for files/folders. * + * > php docblock.php --source=file|folder [ --recursive ] [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] + * * Use from command line - params: - * file/folder - the file or folder you want to docblock (php files) - * -r - to have it recursively go through a folder - * target function - to docblock only a specific method/function name + * --source=path - the file or folder you want to docblock (php files) + * --recursive - optional, recursively go through a folder + * --exclude="p1/ p1/" - options, to exclude a series of paths + * --functions="f1 f2" - optional, space delimited docblock only specific methods/functions + * --dryrun - optional, test the docblock but do not make any changes + * --verbose - optional, output verbose information on each file processed * - * Example: - * php docblock.php target.php targetFunction - * or - * php docblock.php target/dir -r targetFunction + * Examples: + * > php docblock.php --sorce=target.php --functions="targetFunction" + * > php docblock.php --source=target/dir --recursive + * > php docblock.php --source=target/dir --recursive --dryrun + * > php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryrun * * Credit to Sean Coates for the getProtos function, modified a little. * http://seancoates.com/fun-with-the-tokenizer * - * TODOs : + * TODOs: * 1. add all proper docblock properties * 2. better checking for if docblock already exists * 3. docblocking for class properties @@ -26,49 +32,175 @@ * @author Anthony Gentile * @version 0.85 * @link http://agentile.com/docblock/ + * + * @version 0.86 (2014-05-19) + * @link https://github.com/mbrowniebytes/PHP-Docblock-Generator + * + * @version 0.87 (2016-06-16) + * @link https://github.com/vicebas/PHP-Docblock-Generator + * + * @version 0.88 (2020-12-21) + * @link https://github.com/thewitness/PHP-Docblock-Generator + * */ -include("DocBlockGenerator.class.php"); - -use DocBlockGenerator\DocBlockGenerator ; +include('DocBlockGenerator.class.php'); -$argv = empty($_SERVER['argv']) ? array(0 => '') : $_SERVER['argv']; +use DocBlockGenerator\DocBlockGenerator; $current_dir = getcwd(); -$options = array( - 'file_folder' => '', - 'target_function' => '', - 'recursive' => false +$shortopts = 'f::s::rVvHh'; + +$longopts = array( + 'functions::', + 'source::', + 'exclude::', + 'verbose', + 'dryrun', + 'recursive', + 'version', + 'help' ); -foreach ($argv as $k => $arg) { - if ($k !== 0) { - if (strtolower($arg) === '-r') { - $options['recursive'] = true; - } elseif (is_file($arg)) { - $options['file_folder'] = $arg; - } elseif (is_file($current_dir . '/' . $arg)) { - $options['file_folder'] = $current_dir . '/' . $arg; - } elseif (is_dir($arg)) { - $options['file_folder'] = $arg; - } elseif (is_dir($current_dir . '/' . $arg)) { - $options['file_folder'] = $current_dir . '/' . $arg; - } else { - $options['target_function'] = $arg; - } - } +$options = getopt($shortopts, $longopts); + +/* Help and version output */ +if (isset($options['v']) || isset($options['V']) || isset($options['version'])) { + $dbg = new DocBlockGenerator(); + print $dbg->getVersion(); + + exit(0); +} + +if (isset($options['h']) || isset($options['H']) || isset($options['help'])) { + $dbg = new DocBlockGenerator(); + print $dbg->getVersion(); + print $dbg->getHelp(); + + exit(0); } -if (isset($argv[1])) { - if (is_file($options['file_folder']) || is_dir($options['file_folder'])) { - $doc_block_generator = new DocBlockGenerator($options['file_folder'], $options['target_function'], $options['recursive']); - $doc_block_generator->start(); - $doc_block_generator->result(); - } else { - die("\nThis is not a valid file or directory\n"); - } +if (sizeof($options) == 0) { + $dbg = new DocBlockGenerator(); + print $dbg->getVersion(); + print $dbg->getHelp(); + + exit(0); +} + +/* Basic Pre-Checking */ +$recursive = false; +$verbose = false; +$functions = false; +$exclude = false; +$full = false; +$anonymous = false; + +if (isset($options['r']) || isset($options['recursive'])) { + $recursive = true; +} + +if (isset($options['verbose'])) { + $verbose = true; +} + +if (isset($options['dryrun'])) { + $dryrun = true; +} + +if (isset($options['anonymous'])) { + $anonymous = true; +} + +if (isset($options['full'])) { + $full = true; +} +if (isset($options['f']) && isset($options['functions'])) { + print 'FATAL: You can only specify one of -f or --functions' . PHP_EOL; + + exit(1); +} else { + if (isset($options['f'])) { + $functions = $options['f']; + } elseif (isset($options['functions'])) { + $functions = $options['functions']; + } +} + +if (isset($options['s']) && isset($options['source'])) { + print 'FATAL: You can only specify one of -s or --source' . PHP_EOL; + + exit(1); +} else { + if (isset($options['s'])) { + $source = $options['s']; + } elseif (isset($options['source'])) { + $source = $options['source']; + } else { + print 'FATAL: You must specify either -s or --source' . PHP_EOL; + + exit(1); + } +} + +if (isset($options['x']) && isset($options['exclude'])) { + print 'FATAL: You can only specify one of -x or --exclude' . PHP_EOL; + + exit(1); +} else { + if (isset($options['x'])) { + $exclude = $options['x']; + } elseif (isset($options['exclude'])) { + $exclude = $options['exclude']; + } else { + $exclude = false; + } +} + +/* Pre-checking for source. */ +if (is_dir($source) || is_file($source)) { + if (!is_readable($source)) { + print 'FATAL: Source ' . $source . ' is not readable!' . PHP_EOL; + + exit(1); + } + + if (!is_writable($source)) { + print 'FATAL: Source ' . $source . ' is not writable!' . PHP_EOL; + + exit(1); + } } else { - die("\nPlease provide a file or directory as a parameter\n"); + $source = $current_dir . '/' . $source; + + if (is_dir($source) || is_file($source)) { + if (!is_readable($source)) { + print 'FATAL: Source ' . $source . ' is not readable!' . PHP_EOL; + + exit(1); + } + + if (!is_writable($source)) { + print 'FATAL: Source ' . $source . ' is not writable!' . PHP_EOL; + + exit(1); + } + } else { + print 'FATAL: Source ' . $source . ' is not not either a file or directory!' . PHP_EOL; + + exit(1); + } } + +$start = microtime(true); +$dbg = new DocBlockGenerator($source, $functions, $recursive, $exclude, $verbose, $dryrun, $anonymous, $full); +$dbg->preface(); +$dbg->start(); +$dbg->result(); +$end = microtime(true); + +print $dbg->summary($end, $start); + +exit($dbg->exitCode()); From 1b3064496df797d787f75caff69d171551adec10 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:10:46 -0500 Subject: [PATCH 02/17] Update the version number --- src/DocBlockGenerator.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DocBlockGenerator.class.php b/src/DocBlockGenerator.class.php index dde0e7a..09e75ea 100644 --- a/src/DocBlockGenerator.class.php +++ b/src/DocBlockGenerator.class.php @@ -18,7 +18,7 @@ class DocBlockGenerator { public $file_contents; public $log = array(); - private $version = '0.88'; + private $version = '1.1.0'; private $nbr_docblocks = 0; private $total_files = 0; From cfa095a95b01118ff329f6e3b11b77f7a7d8060d Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:12:37 -0500 Subject: [PATCH 03/17] Let's give this a pull --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cf60308..5f306f9 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "thewitness/php-docblock-generator", + "name": "vicebas/php-docblock-generator", "description": "Generate PHPDoc Blocks in files and Folders", "require": {}, "autoload": { From 933511494bb203e5df79336c57938bf4d3d260fe Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:18:04 -0500 Subject: [PATCH 04/17] Updating the readme --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ae42ef4..7dcbb52 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,22 @@ This class will generate DocBlock comment outlines for files/folders. This class will generate docblock outline for files/folders. ```sh -php docblock.php --source=file|folder [ --recursive ] [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] +php docblock.php --source=file|folder [ --recursive ] \ + [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] ``` ## Params Use from command line - params: - --source=path - the file or folder you want to docblock (php files) - --recursive - optional, recursively go through a folder - --exclude="p1/ p1/" - options, to exclude a series of paths - --functions="f1 f2" - optional, space delimited docblock only specific methods/functions - --dryrun - optional, test the docblock but do not make any changes - --verbose - optional, output verbose information on each file processed +--source=path - the file or folder you want to docblock (php files) +--recursive - optional, recursively go through a folder +--exclude="p1/ p1/" - optional, to exclude a series of paths +--functions="f1 f2" - optional, space delimited docblock only specific methods/functions +--anonymous - optional, document anonymous functions as well as normal functions +--full - optional, use full PHPDoc comment blocks instead of short +--dryrun - optional, test the docblock but do not make any changes +--verbose - optional, output verbose information on each file processed ## Examples From 644e4ed609985527ca2bec8012a7e93f24e29191 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:18:44 -0500 Subject: [PATCH 05/17] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7dcbb52..c72a6e3 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ php docblock.php --source=file|folder [ --recursive ] \ Use from command line - params: +``` --source=path - the file or folder you want to docblock (php files) --recursive - optional, recursively go through a folder --exclude="p1/ p1/" - optional, to exclude a series of paths @@ -23,6 +24,7 @@ Use from command line - params: --full - optional, use full PHPDoc comment blocks instead of short --dryrun - optional, test the docblock but do not make any changes --verbose - optional, output verbose information on each file processed +``` ## Examples From 2c7bc0cc1865bbae7eeb993412b892c2778d4f8b Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:20:29 -0500 Subject: [PATCH 06/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c72a6e3..dfd0be3 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,4 @@ php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryru ## Credits Credit to Sean Coates for the getProtos function, modified a little. -http://seancoates.com/fun-with-the-tokenizer +https://seancoates.com/blogs/fun-with-the-tokenizer/ From 2019f56b935f704eb2a52f46f402be7dea3b7d36 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:21:15 -0500 Subject: [PATCH 07/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfd0be3..e708a20 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryru @version 0.87 (2016-06-16) @link https://github.com/vicebas/PHP-Docblock-Generator -@version 0.88 (2020-12-21) +@version 1.1.0 (2020-12-21) @link https://github.com/thewitness/PHP-Docblock-Generator ## Credits From f1290530f5da849cd70b333e15499b1395332cc9 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:22:00 -0500 Subject: [PATCH 08/17] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e708a20..887609a 100644 --- a/README.md +++ b/README.md @@ -44,18 +44,18 @@ php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryru ## ChangeLog -@author Anthony Gentile -@version 0.85 -@link http://agentile.com/docblock/ +author Anthony Gentile +version 0.85 +link http://agentile.com/docblock/ -@version 0.86 (2014-05-19) -@link https://github.com/mbrowniebytes/PHP-Docblock-Generator +version 0.86 (2014-05-19) +link https://github.com/mbrowniebytes/PHP-Docblock-Generator -@version 0.87 (2016-06-16) -@link https://github.com/vicebas/PHP-Docblock-Generator +version 0.87 (2016-06-16) +link https://github.com/vicebas/PHP-Docblock-Generator -@version 1.1.0 (2020-12-21) -@link https://github.com/thewitness/PHP-Docblock-Generator +version 1.1.0 (2020-12-21) +link https://github.com/thewitness/PHP-Docblock-Generator ## Credits From cf0d7d4e3bb7ead775808f2ef57ed14b8860f326 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:22:56 -0500 Subject: [PATCH 09/17] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 887609a..80d2249 100644 --- a/README.md +++ b/README.md @@ -44,18 +44,18 @@ php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryru ## ChangeLog -author Anthony Gentile -version 0.85 -link http://agentile.com/docblock/ +* author Anthony Gentile + version 0.85 + link http://agentile.com/docblock/ -version 0.86 (2014-05-19) -link https://github.com/mbrowniebytes/PHP-Docblock-Generator +* version 0.86 (2014-05-19) + link https://github.com/mbrowniebytes/PHP-Docblock-Generator -version 0.87 (2016-06-16) -link https://github.com/vicebas/PHP-Docblock-Generator +* version 0.87 (2016-06-16) + link https://github.com/vicebas/PHP-Docblock-Generator -version 1.1.0 (2020-12-21) -link https://github.com/thewitness/PHP-Docblock-Generator +* version 1.1.0 (2020-12-21) + link https://github.com/thewitness/PHP-Docblock-Generator ## Credits From 03b517e722e2a545b6982347b065026652ca056e Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:24:45 -0500 Subject: [PATCH 10/17] Update README.md --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 80d2249..4565864 100644 --- a/README.md +++ b/README.md @@ -44,18 +44,13 @@ php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryru ## ChangeLog -* author Anthony Gentile - version 0.85 - link http://agentile.com/docblock/ +* version: 0.85, author, Anthony Gentile, link: http://agentile.com/docblock/ -* version 0.86 (2014-05-19) - link https://github.com/mbrowniebytes/PHP-Docblock-Generator +* version: 0.86 (2014-05-19), link: https://github.com/mbrowniebytes/PHP-Docblock-Generator -* version 0.87 (2016-06-16) - link https://github.com/vicebas/PHP-Docblock-Generator +* version: 0.87 (2016-06-16), link: https://github.com/vicebas/PHP-Docblock-Generator -* version 1.1.0 (2020-12-21) - link https://github.com/thewitness/PHP-Docblock-Generator +* version: 1.1.0 (2020-12-21), link: https://github.com/thewitness/PHP-Docblock-Generator ## Credits From 6ec1f62a1b0eaad9d8dd90ffcac8a869dfa1ac0b Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:25:52 -0500 Subject: [PATCH 11/17] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4565864..ae18b8e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ This class will generate docblock outline for files/folders. ```sh php docblock.php --source=file|folder [ --recursive ] \ - [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] + [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] \ + [ --anonymous ] [ --full ] ``` ## Params From 7deb359aceefee96416f7feceb7213d9733ba854 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 17:26:42 -0500 Subject: [PATCH 12/17] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae18b8e..bd01d37 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ This class will generate docblock outline for files/folders. ```sh php docblock.php --source=file|folder [ --recursive ] \ - [ --exclude="vendor/ tools/" ] [ --dryrun ] [ --versose ] \ + [ --exclude="vendor/ tools/ ..." ] \ + [ --function="f1 f2 ..." ] \ + [ --dryrun ] [ --versose ] \ [ --anonymous ] [ --full ] ``` From 2a1e3f41402a9d17e15dea2d205bb78c93c54a66 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Dec 2020 18:54:23 -0500 Subject: [PATCH 13/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd01d37..21021bd 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This class will generate docblock outline for files/folders. ```sh php docblock.php --source=file|folder [ --recursive ] \ [ --exclude="vendor/ tools/ ..." ] \ - [ --function="f1 f2 ..." ] \ + [ --functions="f1 f2 ..." ] \ [ --dryrun ] [ --versose ] \ [ --anonymous ] [ --full ] ``` From d6effb14a71b0e61ab4dcf32e4a4d0aafcb3d0d0 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Tue, 22 Dec 2020 11:15:40 -0500 Subject: [PATCH 14/17] Adding contextual reference material * This information may be used to further refine the actual development. * It's clear that much of the information contained here can not be automatically inserted into a file * Tools like the php-cs-fixer may be better for backfilling this information once you have properly added DocBlocks to your code. --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21021bd..a807f58 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,68 @@ php docblock.php --source=target/dir --recursive --dryrun php docblock.php --source=target/dir --exclude="vendor/ fa/" --recursive --dryrun ``` -## TODOs: +## TODOs 1. add all proper docblock properties 2. better checking for if docblock already exists 3. docblocking for class properties - 4. try to gather more data for automatic insertion such as for @access + 4. try to gather more data for automatic insertion such as for **@access** + +## DocBlock Tags + +The following is a comprehensive list of tags taken from various locations for contextual references for future development and parameterization of the tool. The sources for this information include the phpDocumentor site and various other sites. No claims of orgininal authorship made here, Credits go to the original authors. + +### Used anywhere +* **@author** — (in any place) — Defines the name or an email of the author who wrote the following code. +* **@copyright** — (in any place) — Is used to put your copyright in the code. +* **@deprecated** — (in any place) — Is a useful tag which means that this element will disappear in the next versions. +* **@example** — (in any place) — Is used for inserting a link to a file or a web page where the example of code usage is shown. +* **@ignore** — (any place) — A DocBlock with this tag won’t be processed when generating documentation, even if there are other tags. +* **@internal** — (any place) — Often used with tag **@api**, to show that the code is used by inner logic of this part of the program. Element with this tag won’t be included in the documentation. +* **@link** — (any place) — Is used for adding links but according to the documentation this tag is not fully supported. +* **@see** — (any place) — Using this tag you can insert links on external resources (just like with @link), but it also allows to put relative links to classes and methods.. +* **@since** — (any place) — You can indicate the version in which the piece of code appeared. +* **@source** — (any place, except the beginning) — with the help of this tag you can place pieces of the source code in the documentation (you set the beginning and the end code line) +* **@todo** — (any place) — The most optimistic tag used by programmers as a reminder of what need to be done in a certain piece of code. IDEhave an ability to detect this tag and group all parts of the code in a separate window which is very convenient for further search. This is the working standard and is used very often. +* **@uses** — (any place) — Is used for displaying the connection between different sections of code. It is similar to **@see**. The difference is that **@see** creates unidirectional link and after you go to a new documentation page you won’t have a backward link while **@uses** gives you a backward navigation link. +* **@version** — (any place) — Denotes the current program version in which this class, method, etc. appeares. + +### Used at Class Level +* **@access** — (class) — Access control for an element. **@access** private prevents documentation of the following element (if enabled). +* **@final** — (class) — Document a class method that should never be overridden in a child class. +* **@license** — (file, class) — Shows the type of license of the written code. +* @method — (class) — Is applied to the class and describes methods processed with function __call(). +* **@package** — (file, class) — Divides code into logical subgroups. When you place classes in the same namespace, you indicate their functional similarity. If classes belong to different namespaces but have the same logical characteristic, they can be grouped using this tag (for example this is the case with classes that all work with customer’s cart but belong to different namespaces). But it is better to avoid such situation. For example, Symfony code style doesn’t use this tag. +* **@property-read, @property-write** — (class) — Both are similar to the previous tag but they process only one magic method __get() or __set(). +* **@property** — (class) — As well as **@method** this tag is placed in the DocBlock of the class, but its function is to describe the properties accessed with the help of magic functions __get() and __set(). + +### Used at Method Level +* **@api** — (method) — Defines the stable public methods, which won’t change their semantics up to the next major release. +* **@param** — (method, function) — Describes the incoming function parameters. It’s worth noticing that if you describe the incoming parameters for a certain function using DocBlocks, you have to describe all parameters, not only one or two. +* **@return** — (method, function) — Is used for describing value returned by the function. +* **@static** — (method) — Document a static property or method. +* **@staticvar** — (method, function) — Document a static variable's use in a function/method +* **@throws** — (method, function) is used for specifying exceptions which can be called out by this function. + +### Used at Package level +* **@category** — (packages) — Specify a category to organize the documented element's package into. +* **@subpackage** — (packages) — Specify sub-package to group classes or functions and defines into. Requires **@package** tag + +### Use at File Level +* **@filesource** — (file) — Is a tag which you can place only at the very beginning of the php file because you can apply this tag only to a file and to include all code to the generated documentation. +* **@license** — (file, class) — shows the type of license of the written code. +* **@package** — (file, class) — divides code into logical subgroups. When you place classes in the same namespace, you indicate their functional similarity. If classes belong to different namespaces but have the same logical characteristic, they can be grouped using this tag (for example this is the case with classes that all work with customer’s cart but belong to different namespaces). But it is better to avoid such situation. For example, Symfony code style doesn’t use this tag. + +### Used at Function Level +* **@param** — (method, function) — Describes the incoming function parameters. It’s worth noticing that if you describe the incoming parameters for a certain function using DocBlocks, you have to describe all parameters, not only one or two. +* **@return** — (method, function) — Is used for describing value returned by the function. You can specify its type and PhpStorm will pick it and give you different tips, but let’s talk about this later. +* **@staticvar** — (method, function) — Document a static variable's use in a function/method +* **@throws** — (method, function) — Is used for specifying exceptions which can be called out by this function. + +### Used at Variable Level +* **@global** — (variable) — Document a global variable, or its use in a function/method. +* **@name** — (variable) — Specify an alias to use for a procedural page or global variable in displayed documentation and linking +* **@var** — (variable) — Is used to specify and to describe variables similar to those used inside the functions and for the class properties. You should distinguish this tag and **@param**. Tag **@param** is used only in DocBlocks for functions and describes the incoming parameters and **@var** is used to describe variables. ## ChangeLog From 5e486f9ebbdd66fcd160f05b793c1ea9cad1ac93 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Tue, 22 Dec 2020 11:26:16 -0500 Subject: [PATCH 15/17] Better organization of tags Before putting this to use, better get the order and spacing right. --- src/DocBlockGenerator.class.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/DocBlockGenerator.class.php b/src/DocBlockGenerator.class.php index 09e75ea..a36a72c 100644 --- a/src/DocBlockGenerator.class.php +++ b/src/DocBlockGenerator.class.php @@ -814,26 +814,23 @@ public function docBlockExists($line) { * @since 0.85 */ public function functionDocBlock($indent, $data) { - $doc_block = $indent.'/**' . PHP_EOL; - $doc_block .= $indent." * {$data['name']}".PHP_EOL; + $doc_block = $indent . '/**' . PHP_EOL; + $doc_block .= $indent . " * {$data['name']}" . PHP_EOL; if (!empty($this->description_placeholder)) { + $doc_block .= $indent . ' *' . PHP_EOL; + $doc_block .= $indent . ' * ' . $this->description_placeholder . PHP_EOL; } - $doc_block .= $indent . ' *' . PHP_EOL; $doc_block .= $indent . ' *' . PHP_EOL; if (isset($data['params'])) { foreach ($data['params'] as $func_param) { - $doc_block .= $indent." * @param ". (isset($func_param['default'])?$this->decodeType($func_param['default']):'type') . " {$func_param['name']}" . PHP_EOL; + $doc_block .= $indent . " * @param " . + (isset($func_param['default']) ? $this->decodeType($func_param['default']) : 'type') . + " {$func_param['name']}" . PHP_EOL; } - - $doc_block .= $indent . ' *' . PHP_EOL; - } - - if (isset($data['return'])) { - $doc_block .= $indent . ' * @return type' . PHP_EOL; } if (!empty($data['access'])) { @@ -846,13 +843,16 @@ public function functionDocBlock($indent, $data) { if ($this->full) { $doc_block .= $indent . ' *' . PHP_EOL; - $doc_block .= $indent . ' * @access' . PHP_EOL; - $doc_block .= $indent . ' * @static' . PHP_EOL; $doc_block .= $indent . ' * @see' . PHP_EOL; $doc_block .= $indent . ' * @since' . PHP_EOL; } - $doc_block .= $indent.' */'.PHP_EOL; + if (isset($data['return'])) { + $doc_block .= $indent . ' *' . PHP_EOL; + $doc_block .= $indent . ' * @return type' . PHP_EOL; + } + + $doc_block .= $indent . ' */' . PHP_EOL; return $doc_block; } From 4f5e06f47c901bd527e86d70db843ac041749e7f Mon Sep 17 00:00:00 2001 From: TheWitness Date: Tue, 22 Dec 2020 11:28:23 -0500 Subject: [PATCH 16/17] Aparently @see and @since come after @return --- src/DocBlockGenerator.class.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DocBlockGenerator.class.php b/src/DocBlockGenerator.class.php index a36a72c..636cb46 100644 --- a/src/DocBlockGenerator.class.php +++ b/src/DocBlockGenerator.class.php @@ -841,15 +841,15 @@ public function functionDocBlock($indent, $data) { $doc_block .= $indent . ' * @static' . PHP_EOL; } - if ($this->full) { + if (isset($data['return'])) { $doc_block .= $indent . ' *' . PHP_EOL; - $doc_block .= $indent . ' * @see' . PHP_EOL; - $doc_block .= $indent . ' * @since' . PHP_EOL; + $doc_block .= $indent . ' * @return type' . PHP_EOL; } - if (isset($data['return'])) { + if ($this->full) { $doc_block .= $indent . ' *' . PHP_EOL; - $doc_block .= $indent . ' * @return type' . PHP_EOL; + $doc_block .= $indent . ' * @see' . PHP_EOL; + $doc_block .= $indent . ' * @since' . PHP_EOL; } $doc_block .= $indent . ' */' . PHP_EOL; From 99fa8c6edcf521cbef0c88496218da4f1a37919f Mon Sep 17 00:00:00 2001 From: TheWitness Date: Tue, 22 Dec 2020 15:39:44 -0500 Subject: [PATCH 17/17] Debugging, more verbosity There were a number of false positives on documentation blocks. After close examination, this appears much better. --- src/DocBlockGenerator.class.php | 122 ++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 36 deletions(-) diff --git a/src/DocBlockGenerator.class.php b/src/DocBlockGenerator.class.php index 636cb46..dca207f 100644 --- a/src/DocBlockGenerator.class.php +++ b/src/DocBlockGenerator.class.php @@ -2,7 +2,7 @@ namespace DocBlockGenerator; class DocBlockGenerator { - public $exts = array('.php', '.php4', '.php5', '.phps', '.inc'); + public $exts = array('.php', '.php4', '.php5', '.phps', '.inc'); public $description_placeholder = 'Insert description here'; // text, if any, to add as placeholder for description @@ -36,6 +36,8 @@ class DocBlockGenerator { private $exist_db_comments = 0; private $exist_nondb_comments = 0; + private $not_documented = 0; + private $nondb_functions = array(); private $db_functions = array(); @@ -95,7 +97,7 @@ public function result() { $str = ''; foreach ($this->log as $log_item) { - $str .= sprintf('| File: %-84s|' . PHP_EOL, $log_item); + $str .= sprintf('| %-120s|' . PHP_EOL, $log_item); } print $str; @@ -116,32 +118,35 @@ public function result() { * @param mixed $start */ public function summary($end, $start) { - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); - printf('| %-90s|' . PHP_EOL, sprintf('DocBlock Generation took %.2f seconds.', $end - $start)); - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); - printf('| %-90s|' . PHP_EOL, sprintf('There were %d files total scanned and %d files excluded.', + printf('| %-120s|' . PHP_EOL, sprintf('DocBlock Generation took %.2f seconds.', $end - $start)); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); + printf('| %-120s|' . PHP_EOL, sprintf('There were %d files total scanned and %d files excluded.', $this->total_files, $this->excluded_files)); - printf('| %-90s|' . PHP_EOL, sprintf('There were %d functions found and %d %s converted.', + printf('| %-120s|' . PHP_EOL, sprintf('There were %d functions found and %d %s converted.', $this->total_functions, $this->converted_functions, ($this->dryrun ? 'would be':'were'))); - printf('| %-90s|' . PHP_EOL, sprintf('There were %d classes found and %d %s converted.', + printf('| %-120s|' . PHP_EOL, sprintf('There were %d classes found and %d %s converted.', $this->total_classes, $this->converted_classes, ($this->dryrun ? 'would be':'were'))); - printf('| %-90s|' . PHP_EOL, sprintf('There were %d existing docblock functions found.', + printf('| %-120s|' . PHP_EOL, sprintf('There were %d functions found with no comments.', + $this->not_documented)); + + printf('| %-120s|' . PHP_EOL, sprintf('There were %d existing docblock functions found.', $this->exist_db_comments)); - printf('| %-90s|' . PHP_EOL, sprintf('WARNING: There were %d existing non-docblock functions found.', + printf('| %-120s|' . PHP_EOL, sprintf('WARNING: There were %d existing non-docblock functions found.', $this->exist_nondb_comments)); if ($this->dryrun) { - printf('| %-90s|' . PHP_EOL, sprintf('There would be %d read/write errors expected.', $this->errors)); + printf('| %-120s|' . PHP_EOL, sprintf('There would be %d read/write errors expected.', $this->errors)); } else { - printf('| %-90s|' . PHP_EOL, sprintf('There were %d read/write errors.', $this->errors)); + printf('| %-120s|' . PHP_EOL, sprintf('There were %d read/write errors.', $this->errors)); } - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL . PHP_EOL); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); } /** @@ -158,20 +163,20 @@ public function summary($end, $start) { * @param mixed $start */ public function preface() { - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); - printf('| %-90s|' . PHP_EOL, sprintf('PHP DocBlock Generator - Starting %s', ($this->dryrun ? '- Dry Run Only':''))); - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); - printf('| %-90s|' . PHP_EOL, sprintf('Processing Starting - Settings below'),); - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); - printf('| %-90s|' . PHP_EOL, sprintf('Target Path: %s', $this->target)); - printf('| %-90s|' . PHP_EOL, sprintf('Recursion is: %s', $this->recursive ? 'Enabled':'Disabled')); - printf('| %-90s|' . PHP_EOL, sprintf('File Exclusions: %s', ($this->exclude ? implode(', ', $this->exclude) : 'None Excluded'))); - printf('| %-90s|' . PHP_EOL, sprintf('Included Functions: %s', ($this->functions ? implode(', ', $this->functions) : 'All Functions'))); - printf('| %-90s|' . PHP_EOL, sprintf('Anonymous Functions: %s', ($this->anonymous ? 'Included' : 'Skipped'))); - printf('| %-90s|' . PHP_EOL, sprintf('PHPDoc Comment Style: %s', ($this->full ? 'Full' : 'Short'))); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); + printf('| %-120s|' . PHP_EOL, sprintf('PHP DocBlock Generator - Starting %s', ($this->dryrun ? '- Dry Run Only':''))); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); + printf('| %-120s|' . PHP_EOL, sprintf('Processing Starting - Settings below'),); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); + printf('| %-120s|' . PHP_EOL, sprintf('Target Path: %s', $this->target)); + printf('| %-120s|' . PHP_EOL, sprintf('Recursion is: %s', $this->recursive ? 'Enabled':'Disabled')); + printf('| %-120s|' . PHP_EOL, sprintf('File Exclusions: %s', ($this->exclude ? implode(', ', $this->exclude) : 'None Excluded'))); + printf('| %-120s|' . PHP_EOL, sprintf('Included Functions: %s', ($this->functions ? implode(', ', $this->functions) : 'All Functions'))); + printf('| %-120s|' . PHP_EOL, sprintf('Anonymous Functions: %s', ($this->anonymous ? 'Included' : 'Skipped'))); + printf('| %-120s|' . PHP_EOL, sprintf('PHPDoc Comment Style: %s', ($this->full ? 'Full' : 'Short'))); if ($this->verbose) { - printf('+-------------------------------------------------------------------------------------------+' . PHP_EOL); + printf('+' . str_repeat('-', 121) . '+' . PHP_EOL); } } @@ -261,7 +266,7 @@ public function excludeCheck($target) { if (is_array($this->exclude) && sizeof($this->exclude)) { foreach ($this->exclude as $path) { if (strpos($target, $path) !== false) { - $this->log[] = "{$target} - is Excluded."; + //$this->log[] = "{$target} - is Excluded."; $this->excluded_files++; return true; @@ -346,20 +351,28 @@ public function fileCheck($target) { */ public function fileDocBlock() { $this->nbr_docblocks = 0; - $this->file_contents = file_get_contents($this->target); + + $this->log[] = "NOTE: Tokenizing {$this->target}"; + + $this->file_contents = file_get_contents($this->target); list($funcs, $classes) = $this->getProtos(); + $this->log[] = "NOTE: Opening File for Post Processing {$this->target}"; + $handle = fopen($this->target, 'r'); if ($contents = fread($handle, filesize($this->target))) { $contents = explode(PHP_EOL, $contents); $contents = $this->docBlock($contents, $funcs, $classes, $this->functions); $contents = implode(PHP_EOL, $contents); + fclose($handle); + $this->log[] = "NOTE: Closing File after Post Processing {$this->target}"; + if ($this->nbr_docblocks == 0) { - $this->log[] = "{$this->target} - Nothing to DocBlock"; + $this->log[] = "NOTE: Nothing to DocBlock for {$this->target}"; $this->skipped_files++; } else { if (!$this->dryrun) { @@ -368,13 +381,13 @@ public function fileDocBlock() { if (fwrite($handle, $contents)) { $this->converted_functions += $this->nbr_docblocks; - $this->log[] = "{$this->target} - DocBlocked " . $this->nbr_docblocks; + $this->log[] = "NOTE: DocBlocked " . $this->nbr_docblocks . " in {$this->target}"; fclose($handle); return; } else { fclose($handle); - $this->log[] = "WARNING: Could not write new content. - Check Permissions"; + $this->log[] = "WARNING: Write error for {$this->target} - Check Permissions"; $this->errors++; return; @@ -389,7 +402,7 @@ public function fileDocBlock() { } } else { fclose($handle); - $this->log[] = "Could not get file contents.\nCheck Permissions"; + $this->log[] = "WARNING: Read error for {$this->target} - Check Permissions"; $this->errors++; return; @@ -419,13 +432,19 @@ public function getProtos() { for ($i = 0; $i < $count; $i++) { if (is_array($tokens[$i]) && $tokens[$i][0] == T_COMMENT) { - $comment = true; - $dcomment = false; + // Ignore the first comment if it includes copyright + if (stripos($tokens[$i][1], 'copyright') === false) { + $comment = true; + $dcomment = false; + } } if (is_array($tokens[$i]) && $tokens[$i][0] == T_DOC_COMMENT) { - $dcomment = true; - $comment = false; + // Ignore the first comment if it includes copyright + if (stripos($tokens[$i][1], 'copyright') === false) { + $dcomment = true; + $comment = false; + } } if (is_array($tokens[$i]) && $tokens[$i][0] == T_RETURN) { @@ -445,6 +464,10 @@ public function getProtos() { if ($comment) { $this->exist_nondb_comments++; + if ($this->verbose) { + $this->log[] = "WARNING: Class '{$curr_class}' found in {$this->target} is commented in a Non DocBlock fashion"; + } + $comment = false; $dcomment = false; @@ -452,6 +475,10 @@ public function getProtos() { } elseif ($dcomment) { $this->exist_db_comments++; + if ($this->verbose) { + $this->log[] = "NOTE: Class '{$curr_class}' found is commented in a DocBlock fashion"; + } + $comment = false; $dcomment = false; @@ -490,11 +517,16 @@ public function getProtos() { $next_by_ref = false; $this_func = array(); $func_status = array(); - $curr_func = $tokens[$i][1]; + $curr_func = $tokens[$i+2][1]; if ($comment) { $this->exist_nondb_comments++; + if ($this->verbose) { + $this->log[] = "WARNING: Function '{$curr_func}' in {$this->target} is commented in a Non DocBlock fashion"; + } + print "WARNING: Function '{$curr_func}' in {$this->target} is commented in a Non DocBlock fashion" . PHP_EOL; + $comment = false; $dcomment = false; @@ -502,10 +534,20 @@ public function getProtos() { } elseif ($dcomment) { $this->exist_db_comments++; + if ($this->verbose) { + $this->log[] = "NOTE: Function '{$curr_func}' found is commented in a DocBlock fashion"; + } + $comment = false; $dcomment = false; $this->db_functions[$curr_func] = $curr_func; + } else { + $this->not_documented++; + + if ($this->verbose) { + $this->log[] = "WARNING: Function '{$curr_func}' found is not commented."; + } } if (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == 'static') { @@ -613,6 +655,12 @@ public function getProtos() { --$class_depth; } + /* Reset the comment tracker when we get to the end of a function block */ + if (!is_array($tokens[$i])) { + $comment = false; + $dcomment = false; + } + if ($class_depth == 0) { $curr_class = ''; } @@ -859,7 +907,9 @@ public function functionDocBlock($indent, $data) { /** * Decode the parameter type + * * @param type $type + * * @return string */ public function decodeType($type) {