├── LICENSE ├── README.md ├── bin └── phpdoc-md ├── composer.json ├── docs.md ├── src └── PHPDocsMD │ ├── ClassEntity.php │ ├── ClassEntityFactory.php │ ├── CodeEntity.php │ ├── Console │ ├── CLI.php │ └── PHPDocsMDCommand.php │ ├── DocInfo.php │ ├── DocInfoExtractor.php │ ├── FunctionEntity.php │ ├── FunctionFinder.php │ ├── MDTableGenerator.php │ ├── ParamEntity.php │ ├── Reflector.php │ ├── ReflectorInterface.php │ ├── TableGenerator.php │ ├── UseInspector.php │ └── Utils.php └── test ├── ExampleClass.php ├── MDTableGeneratorTest.php ├── ReflectorTest.php └── UseInspectorTest.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 Victor Jonsson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-Markdown-Documentation-Generator 2 | 3 | Documentation is just as important as the code it's refering to. With this command line tool 4 | you will be able to write your documentation once, and only once! 5 | 6 | This project will write a single-page markdown-formatted API document based on the DocBlock comments in your source code. 7 | 8 | ![Travis](https://travis-ci.org/victorjonsson/PHP-Markdown-Documentation-Generator.svg) 9 | 10 | ### Example 11 | 12 | Let's say you have your PHP classes in a directory named "src". Each class has its own file that is named after the class. 13 | 14 | ``` 15 | - src/ 16 | - MyObject.php 17 | - OtherObject.php 18 | ``` 19 | 20 | Write your code documentation following the standard set by [phpdoc](http://www.phpdoc.org/). 21 | 22 | ```php 23 | namespace Acme; 24 | 25 | /** 26 | * This is a description of this class 27 | */ 28 | class MyObject { 29 | 30 | /** 31 | * This is a function description 32 | * @param string $str 33 | * @param array $arr 34 | * @return Acme\OtherObject 35 | */ 36 | function someFunc($str, $arr=array()) { 37 | 38 | } 39 | } 40 | ``` 41 | 42 | Then, running `$ phpdoc-md generate src > api.md` will write your API documentation to the file api.md. 43 | 44 | [Here you can see a rendered example](https://github.com/victorjonsson/PHP-Markdown-Documentation-Generator/blob/master/docs.md) 45 | 46 | Only public and protected functions will be a part of the documentation, but you can also add `@ignore` to any function or class to exclude it from the docs. Phpdoc-md will try to guess the return type of functions that don't explicitly declare one. The program uses reflection to get as much information as possible out of the code so that functions that are missing DocBlock comments will still be included in the generated documentation. 47 | 48 | ### Requirements 49 | 50 | - PHP version >= 5.3.2 51 | - Reflection must be enabled in php.ini 52 | - Each class must be defined in its own file with the file name being the same as the class name 53 | - The project should use [Composer](https://getcomposer.org/) 54 | 55 | ### Installation / Usage 56 | 57 | This command line tool can be installed using [composer](https://getcomposer.org/). 58 | 59 | From the local working directory of the project that you would like to document, run: 60 | ``` 61 | $ composer require --dev victorjonsson/markdowndocs 62 | ``` 63 | This will add victorjonsson/markdowndocs to the `require-dev` section of your project's composer.json file. The phpdoc-md executable will automatically be copied to your project's `vendor/bin` directory. 64 | 65 | ##### Generating docs 66 | 67 | The `generate` command generates your project's API documentation file. The command line tool needs to know whether you want to generate docs for a certain class, or if it should process every class in a specified directory search path. 68 | 69 | ``` 70 | # Generate docs for a certain class 71 | $ ./vendor/bin/phpdoc-md generate Acme\\NS\\MyClass 72 | 73 | # Generate docs for several classes (comma separated) 74 | $ ./vendor/bin/phpdoc-md generate Acme\\NS\\MyClass,Acme\\OtherNS\\OtherClass 75 | 76 | # Generate docs for all classes in a source directory 77 | $ ./vendor/bin/phpdoc-md generate includes/src 78 | 79 | # Generate docs for all classes in a source directory and send output to the file api.md 80 | $ ./vendor/bin/phpdoc-md generate includes/src > api.md 81 | ``` 82 | 83 | * Note that any class to be documented must be loadable using the autoloader provided by composer. * 84 | 85 | ##### Bootstrapping 86 | 87 | If you are not using the composer autoloader, or if there is something else that needs to be done before your classes can be instantiated, then you may request phpdoc-md to load a php bootstrap file prior to generating the docs 88 | 89 | `$ ./vendor/bin/phpdoc-md generate --bootstrap=includes/init.php includes/src > api.md` 90 | 91 | ##### Excluding directories 92 | 93 | You can tell the command line tool to ignore certain directories in your class path by using the `--ignore` option. 94 | 95 | `$ ./phpdoc-md generate --ignore=test,examples includes/src > api.md` 96 | 97 | -------------------------------------------------------------------------------- /bin/phpdoc-md: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 16 | exit($code); 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "victorjonsson/markdowndocs", 3 | "description": "Command line tool for generating markdown-formatted class documentation", 4 | "minimum-stability": "dev", 5 | "homepage" : "https://github.com/victorjonsson/PHP-Markdown-Documentation-Generator", 6 | "license" : "MIT", 7 | "version" : "1.3.8", 8 | "authors": [{ 9 | "name": "Victor Jonsson", 10 | "email": "kontakt@victorjonsson.se" 11 | }], 12 | "require" : { 13 | "php" : ">=5.5.0", 14 | "symfony/console": ">=2.6" 15 | }, 16 | "require-dev" : { 17 | "phpunit/phpunit": "9.5" 18 | }, 19 | "bin":["bin/phpdoc-md"], 20 | "autoload": { 21 | "psr-0": {"PHPDocsMD": "src/"} 22 | }, 23 | "autoload-dev": { 24 | "psr-4": { 25 | "Acme\\": "test" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | ## Table of contents 2 | 3 | - [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) 4 | - [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) 5 | - [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) 6 | - [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) 7 | - [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) 8 | - [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) 9 | - [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) 10 | - [\PHPDocsMD\MDTableGenerator](#class-phpdocsmdmdtablegenerator) 11 | - [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity) 12 | - [\PHPDocsMD\Reflector](#class-phpdocsmdreflector) 13 | - [\PHPDocsMD\ReflectorInterface (interface)](#interface-phpdocsmdreflectorinterface) 14 | - [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) 15 | - [\PHPDocsMD\Utils](#class-phpdocsmdutils) 16 | - [\PHPDocsMD\Console\CLI](#class-phpdocsmdconsolecli) 17 | - [\PHPDocsMD\Console\PHPDocsMDCommand](#class-phpdocsmdconsolephpdocsmdcommand) 18 | 19 |
20 | 21 | ### Class: \PHPDocsMD\ClassEntity 22 | 23 | > Object describing a class or an interface 24 | 25 | | Visibility | Function | 26 | |:-----------|:---------| 27 | | public | generateAnchor() : string
Generates an anchor link out of the generated title (see generateTitle) | 28 | | public | generateTitle(string $format=`'%label%: %name% %extra%'`) : string
Generate a title describing the class this object is referring to | 29 | | public | getExtends() : string | 30 | | public | getFunctions() : [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] | 31 | | public | getInterfaces() : array | 32 | | public | hasIgnoreTag(bool $toggle=null) : bool | 33 | | public | isAbstract(bool $toggle=null) : bool | 34 | | public | isInterface(bool $toggle=null) : bool | 35 | | public | isNative(bool $toggle=null) : bool | 36 | | public | isSame(string/object $class) : bool
Check whether this object is referring to given class name or object instance | 37 | | public | setExtends(string $extends) : void | 38 | | public | setFunctions([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] $functions) : void | 39 | | public | setInterfaces(array $implements) : void | 40 | | public | setName(string $name) : void | 41 | 42 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 43 | 44 |
45 | 46 | ### Class: \PHPDocsMD\ClassEntityFactory 47 | 48 | > Class capable of creating ClassEntity objects 49 | 50 | | Visibility | Function | 51 | |:-----------|:---------| 52 | | public | __construct([\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor) : void | 53 | | public | create([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflection) : mixed | 54 | 55 |
56 | 57 | ### Class: \PHPDocsMD\CodeEntity 58 | 59 | > Object describing a piece of code 60 | 61 | | Visibility | Function | 62 | |:-----------|:---------| 63 | | public | getDeprecationMessage() : string | 64 | | public | getDescription() : string | 65 | | public | getExample() : string | 66 | | public | getName() : string | 67 | | public | isDeprecated(bool $toggle=null) : void/bool | 68 | | public | setDeprecationMessage(string $deprecationMessage) : void | 69 | | public | setDescription(string $description) : void | 70 | | public | setExample(string $example) : void | 71 | | public | setName(string $name) : void | 72 | 73 |
74 | ### Class: \PHPDocsMD\DocInfo 75 | 76 | > Class containing information about a function/class that's being made available via a comment block 77 | 78 | | Visibility | Function | 79 | |:-----------|:---------| 80 | | public | __construct(array $data) : void | 81 | | public | getDeprecationMessage() : string | 82 | | public | getDescription() : string | 83 | | public | getExample() : string | 84 | | public | getParameterInfo(string $name) : array | 85 | | public | getParameters() : array | 86 | | public | getReturnType() : string | 87 | | public | shouldBeIgnored() : bool | 88 | | public | shouldInheritDoc() : bool | 89 | 90 |
91 | ### Class: \PHPDocsMD\DocInfoExtractor 92 | 93 | > Class that can extract information from a function/class comment 94 | 95 | | Visibility | Function | 96 | |:-----------|:---------| 97 | | public | applyInfoToEntity([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection, [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $docInfo, [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) $code) : void | 98 | | public | extractInfo([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection) : [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) | 99 | 100 |
101 | ### Class: \PHPDocsMD\FunctionEntity 102 | 103 | > Object describing a function 104 | 105 | | Visibility | Function | 106 | |:-----------|:---------| 107 | | public | getClass() : string | 108 | | public | getParams() : [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] | 109 | | public | getReturnType() : string | 110 | | public | getVisibility() : string | 111 | | public | hasParams() : bool | 112 | | public | isAbstract(bool $toggle=null) : bool | 113 | | public | isReturningNativeClass(bool $toggle=null) : bool | 114 | | public | isStatic(bool $toggle=null) : bool | 115 | | public | setClass(string $class) : void | 116 | | public | setParams([\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] $params) : void | 117 | | public | setReturnType(string $returnType) : void | 118 | | public | setVisibility(string $visibility) : void | 119 | 120 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 121 | 122 |
123 | ### Class: \PHPDocsMD\FunctionFinder 124 | 125 | > Find a specific function in a class or an array of classes 126 | 127 | | Visibility | Function | 128 | |:-----------|:---------| 129 | | public | find(string $methodName, string $className) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 130 | | public | findInClasses(string $methodName, array $classes) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 131 | 132 |
133 | ### Class: \PHPDocsMD\MDTableGenerator 134 | 135 | > Class that can create a markdown-formatted table describing class functions referred to via FunctionEntity objects 136 | 137 | ###### Example 138 | ```php 139 | openTable(); 142 | foreach($classEntity->getFunctions() as $func) { 143 | $generator->addFunc( $func ); 144 | } 145 | echo $generator->getTable(); 146 | ``` 147 | 148 | | Visibility | Function | 149 | |:-----------|:---------| 150 | | public | addFunc([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) $func) : string
Generates a markdown formatted table row with information about given function. Then adds the row to the table and returns the markdown formatted string. | 151 | | public | appendExamplesToEndOfTable(bool $toggle) : void
All example comments found while generating the table will be appended to the end of the table. Set $toggle to false to prevent this behaviour | 152 | | public | doDeclareAbstraction(bool $toggle) : void
Toggle whether or not methods being abstract (or part of an interface) should be declared as abstract in the table | 153 | | public static | formatExampleComment(string $example) : string
Create a markdown-formatted code view out of an example comment | 154 | | public | getTable() : string | 155 | | public | openTable() : void
Begin generating a new markdown-formatted table | 156 | 157 |
158 | ### Class: \PHPDocsMD\ParamEntity 159 | 160 | > Object describing a function parameter 161 | 162 | | Visibility | Function | 163 | |:-----------|:---------| 164 | | public | getDefault() : boolean | 165 | | public | getNativeClassType() : string/null | 166 | | public | getType() : string | 167 | | public | setDefault(boolean $default) : void | 168 | | public | setType(string $type) : void | 169 | 170 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 171 | 172 |
173 | ### Class: \PHPDocsMD\Reflector 174 | 175 | > Class that can compute ClassEntity objects out of real classes 176 | 177 | | Visibility | Function | 178 | |:-----------|:---------| 179 | | public | __construct(string $className, [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) $functionFinder=null, [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor=null, [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) $useInspector=null, [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) $classEntityFactory=null) : void | 180 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 181 | | public static | getParamType([\ReflectionParameter](http://php.net/manual/en/class.reflectionparameter.php) $refParam) : string
Tries to find out if the type of the given parameter. Will return empty string if not possible. | 182 | | protected | createFunctionEntity([\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class, array $useStatements) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 183 | | protected | shouldIgnoreFunction([\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $info, [\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class) : bool | 184 | ###### Examples of Reflector::getParamType() 185 | ```php 186 | getMethods() as $method ) { 189 | foreach($method->getParameters() as $param) { 190 | $name = $param->getName(); 191 | $type = Reflector::getParamType($param); 192 | printf("%s = %s\n", $name, $type); 193 | } 194 | } 195 | ``` 196 | 197 | *This class implements [\PHPDocsMD\ReflectorInterface](#interface-phpdocsmdreflectorinterface)* 198 | 199 |
200 | ### Interface: \PHPDocsMD\ReflectorInterface 201 | 202 | > Interface for classes that can compute ClassEntity objects 203 | 204 | | Visibility | Function | 205 | |:-----------|:---------| 206 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 207 | 208 |
209 | ### Class: \PHPDocsMD\UseInspector 210 | 211 | > Class that can extract all use statements in a file 212 | 213 | | Visibility | Function | 214 | |:-----------|:---------| 215 | | public | getUseStatements([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflectionClass) : array | 216 | | public | getUseStatementsInFile(string $filePath) : array | 217 | | public | getUseStatementsInString(string $content) : string[] | 218 | 219 |
220 | ### Class: \PHPDocsMD\Utils 221 | 222 | | Visibility | Function | 223 | |:-----------|:---------| 224 | | public static | getClassBaseName(string $fullClassName) : string | 225 | | public static | isClassReference(string $typeDeclaration) : bool | 226 | | public static | isNativeClassReference(mixed $typeDeclaration) : bool | 227 | | public static | sanitizeClassName(string $name) : string | 228 | | public static | sanitizeDeclaration(string $typeDeclaration, string $currentNameSpace, string $delimiter=`'|'`) : string | 229 | 230 |
231 | ### Class: \PHPDocsMD\Console\CLI 232 | 233 | > Command line interface used to extract markdown-formatted documentation from classes 234 | 235 | | Visibility | Function | 236 | |:-----------|:---------| 237 | | public | __construct() : void | 238 | | public | run(\Symfony\Component\Console\Input\InputInterface $input=null, \Symfony\Component\Console\Output\OutputInterface $output=null) : int | 239 | 240 | *This class extends \Symfony\Component\Console\Application* 241 | 242 |
243 | ### Class: \PHPDocsMD\Console\PHPDocsMDCommand 244 | 245 | > Console command used to extract markdown-formatted documentation from classes 246 | 247 | | Visibility | Function | 248 | |:-----------|:---------| 249 | | public | extractClassNameFromLine(string $type, string $line) : string | 250 | | protected | configure() : void | 251 | | protected | execute(\Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output) : int/null | 252 | 253 | *This class extends \Symfony\Component\Console\Command\Command* 254 | 255 | ## Table of contents 256 | 257 | - [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) 258 | - [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) 259 | - [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) 260 | - [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) 261 | - [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) 262 | - [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) 263 | - [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) 264 | - [\PHPDocsMD\MDTableGenerator](#class-phpdocsmdmdtablegenerator) 265 | - [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity) 266 | - [\PHPDocsMD\Reflector](#class-phpdocsmdreflector) 267 | - [\PHPDocsMD\ReflectorInterface (interface)](#interface-phpdocsmdreflectorinterface) 268 | - [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) 269 | - [\PHPDocsMD\Utils](#class-phpdocsmdutils) 270 | - [\PHPDocsMD\Console\CLI](#class-phpdocsmdconsolecli) 271 | - [\PHPDocsMD\Console\PHPDocsMDCommand](#class-phpdocsmdconsolephpdocsmdcommand) 272 | 273 |
274 | ### Class: \PHPDocsMD\ClassEntity 275 | 276 | > Object describing a class or an interface 277 | 278 | | Visibility | Function | 279 | |:-----------|:---------| 280 | | public | generateAnchor() : string
Generates an anchor link out of the generated title (see generateTitle) | 281 | | public | generateTitle(string $format=`'%label%: %name% %extra%'`) : string
Generate a title describing the class this object is referring to | 282 | | public | getExtends() : string | 283 | | public | getFunctions() : [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] | 284 | | public | getInterfaces() : array | 285 | | public | hasIgnoreTag(bool $toggle=null) : bool | 286 | | public | isAbstract(bool $toggle=null) : bool | 287 | | public | isInterface(bool $toggle=null) : bool | 288 | | public | isNative(bool $toggle=null) : bool | 289 | | public | isSame(string/object $class) : bool
Check whether this object is referring to given class name or object instance | 290 | | public | setExtends(string $extends) : void | 291 | | public | setFunctions([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] $functions) : void | 292 | | public | setInterfaces(array $implements) : void | 293 | | public | setName(string $name) : void | 294 | 295 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 296 | 297 |
298 | ### Class: \PHPDocsMD\ClassEntityFactory 299 | 300 | > Class capable of creating ClassEntity objects 301 | 302 | | Visibility | Function | 303 | |:-----------|:---------| 304 | | public | __construct([\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor) : void | 305 | | public | create([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflection) : mixed | 306 | 307 |
308 | ### Class: \PHPDocsMD\CodeEntity 309 | 310 | > Object describing a piece of code 311 | 312 | | Visibility | Function | 313 | |:-----------|:---------| 314 | | public | getDeprecationMessage() : string | 315 | | public | getDescription() : string | 316 | | public | getExample() : string | 317 | | public | getName() : string | 318 | | public | isDeprecated(bool $toggle=null) : void/bool | 319 | | public | setDeprecationMessage(string $deprecationMessage) : void | 320 | | public | setDescription(string $description) : void | 321 | | public | setExample(string $example) : void | 322 | | public | setName(string $name) : void | 323 | 324 |
325 | ### Class: \PHPDocsMD\DocInfo 326 | 327 | > Class containing information about a function/class that's being made available via a comment block 328 | 329 | | Visibility | Function | 330 | |:-----------|:---------| 331 | | public | __construct(array $data) : void | 332 | | public | getDeprecationMessage() : string | 333 | | public | getDescription() : string | 334 | | public | getExample() : string | 335 | | public | getParameterInfo(string $name) : array | 336 | | public | getParameters() : array | 337 | | public | getReturnType() : string | 338 | | public | shouldBeIgnored() : bool | 339 | | public | shouldInheritDoc() : bool | 340 | 341 |
342 | ### Class: \PHPDocsMD\DocInfoExtractor 343 | 344 | > Class that can extract information from a function/class comment 345 | 346 | | Visibility | Function | 347 | |:-----------|:---------| 348 | | public | applyInfoToEntity([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection, [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $docInfo, [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) $code) : void | 349 | | public | extractInfo([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection) : [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) | 350 | 351 |
352 | ### Class: \PHPDocsMD\FunctionEntity 353 | 354 | > Object describing a function 355 | 356 | | Visibility | Function | 357 | |:-----------|:---------| 358 | | public | getClass() : string | 359 | | public | getParams() : [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] | 360 | | public | getReturnType() : string | 361 | | public | getVisibility() : string | 362 | | public | hasParams() : bool | 363 | | public | isAbstract(bool $toggle=null) : bool | 364 | | public | isReturningNativeClass(bool $toggle=null) : bool | 365 | | public | isStatic(bool $toggle=null) : bool | 366 | | public | setClass(string $class) : void | 367 | | public | setParams([\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] $params) : void | 368 | | public | setReturnType(string $returnType) : void | 369 | | public | setVisibility(string $visibility) : void | 370 | 371 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 372 | 373 |
374 | ### Class: \PHPDocsMD\FunctionFinder 375 | 376 | > Find a specific function in a class or an array of classes 377 | 378 | | Visibility | Function | 379 | |:-----------|:---------| 380 | | public | find(string $methodName, string $className) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 381 | | public | findInClasses(string $methodName, array $classes) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 382 | 383 |
384 | ### Class: \PHPDocsMD\MDTableGenerator 385 | 386 | > Class that can create a markdown-formatted table describing class functions referred to via FunctionEntity objects 387 | 388 | ###### Example 389 | ```php 390 | openTable(); 393 | foreach($classEntity->getFunctions() as $func) { 394 | $generator->addFunc( $func ); 395 | } 396 | echo $generator->getTable(); 397 | ``` 398 | 399 | | Visibility | Function | 400 | |:-----------|:---------| 401 | | public | addFunc([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) $func) : string
Generates a markdown formatted table row with information about given function. Then adds the row to the table and returns the markdown formatted string. | 402 | | public | appendExamplesToEndOfTable(bool $toggle) : void
All example comments found while generating the table will be appended to the end of the table. Set $toggle to false to prevent this behaviour | 403 | | public | doDeclareAbstraction(bool $toggle) : void
Toggle whether or not methods being abstract (or part of an interface) should be declared as abstract in the table | 404 | | public static | formatExampleComment(string $example) : string
Create a markdown-formatted code view out of an example comment | 405 | | public | getTable() : string | 406 | | public | openTable() : void
Begin generating a new markdown-formatted table | 407 | 408 |
409 | ### Class: \PHPDocsMD\ParamEntity 410 | 411 | > Object describing a function parameter 412 | 413 | | Visibility | Function | 414 | |:-----------|:---------| 415 | | public | getDefault() : boolean | 416 | | public | getNativeClassType() : string/null | 417 | | public | getType() : string | 418 | | public | setDefault(boolean $default) : void | 419 | | public | setType(string $type) : void | 420 | 421 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 422 | 423 |
424 | ### Class: \PHPDocsMD\Reflector 425 | 426 | > Class that can compute ClassEntity objects out of real classes 427 | 428 | | Visibility | Function | 429 | |:-----------|:---------| 430 | | public | __construct(string $className, [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) $functionFinder=null, [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor=null, [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) $useInspector=null, [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) $classEntityFactory=null) : void | 431 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 432 | | public static | getParamType([\ReflectionParameter](http://php.net/manual/en/class.reflectionparameter.php) $refParam) : string
Tries to find out if the type of the given parameter. Will return empty string if not possible. | 433 | | protected | createFunctionEntity([\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class, array $useStatements) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 434 | | protected | shouldIgnoreFunction([\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $info, [\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class) : bool | 435 | ###### Examples of Reflector::getParamType() 436 | ```php 437 | getMethods() as $method ) { 440 | foreach($method->getParameters() as $param) { 441 | $name = $param->getName(); 442 | $type = Reflector::getParamType($param); 443 | printf("%s = %s\n", $name, $type); 444 | } 445 | } 446 | ``` 447 | 448 | *This class implements [\PHPDocsMD\ReflectorInterface](#interface-phpdocsmdreflectorinterface)* 449 | 450 |
451 | ### Interface: \PHPDocsMD\ReflectorInterface 452 | 453 | > Interface for classes that can compute ClassEntity objects 454 | 455 | | Visibility | Function | 456 | |:-----------|:---------| 457 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 458 | 459 |
460 | ### Class: \PHPDocsMD\UseInspector 461 | 462 | > Class that can extract all use statements in a file 463 | 464 | | Visibility | Function | 465 | |:-----------|:---------| 466 | | public | getUseStatements([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflectionClass) : array | 467 | | public | getUseStatementsInFile(string $filePath) : array | 468 | | public | getUseStatementsInString(string $content) : string[] | 469 | 470 |
471 | ### Class: \PHPDocsMD\Utils 472 | 473 | | Visibility | Function | 474 | |:-----------|:---------| 475 | | public static | getClassBaseName(string $fullClassName) : string | 476 | | public static | isClassReference(string $typeDeclaration) : bool | 477 | | public static | isNativeClassReference(mixed $typeDeclaration) : bool | 478 | | public static | sanitizeClassName(string $name) : string | 479 | | public static | sanitizeDeclaration(string $typeDeclaration, string $currentNameSpace, string $delimiter=`'|'`) : string | 480 | 481 |
482 | ### Class: \PHPDocsMD\Console\CLI 483 | 484 | > Command line interface used to extract markdown-formatted documentation from classes 485 | 486 | | Visibility | Function | 487 | |:-----------|:---------| 488 | | public | __construct() : void | 489 | | public | run(\Symfony\Component\Console\Input\InputInterface $input=null, \Symfony\Component\Console\Output\OutputInterface $output=null) : int | 490 | 491 | *This class extends \Symfony\Component\Console\Application* 492 | 493 |
494 | ### Class: \PHPDocsMD\Console\PHPDocsMDCommand 495 | 496 | > Console command used to extract markdown-formatted documentation from classes 497 | 498 | | Visibility | Function | 499 | |:-----------|:---------| 500 | | public | extractClassNameFromLine(string $type, string $line) : string | 501 | | protected | configure() : void | 502 | | protected | execute(\Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output) : int/null | 503 | 504 | *This class extends \Symfony\Component\Console\Command\Command* 505 | 506 | ## Table of contents 507 | 508 | - [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) 509 | - [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) 510 | - [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) 511 | - [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) 512 | - [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) 513 | - [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) 514 | - [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) 515 | - [\PHPDocsMD\MDTableGenerator](#class-phpdocsmdmdtablegenerator) 516 | - [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity) 517 | - [\PHPDocsMD\Reflector](#class-phpdocsmdreflector) 518 | - [\PHPDocsMD\ReflectorInterface (interface)](#interface-phpdocsmdreflectorinterface) 519 | - [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) 520 | - [\PHPDocsMD\Utils](#class-phpdocsmdutils) 521 | - [\PHPDocsMD\Console\CLI](#class-phpdocsmdconsolecli) 522 | - [\PHPDocsMD\Console\PHPDocsMDCommand](#class-phpdocsmdconsolephpdocsmdcommand) 523 | 524 |
525 | 526 | ### Class: \PHPDocsMD\ClassEntity 527 | 528 | > Object describing a class or an interface 529 | 530 | | Visibility | Function | 531 | |:-----------|:---------| 532 | | public | generateAnchor() : string
Generates an anchor link out of the generated title (see generateTitle) | 533 | | public | generateTitle(string $format=`'%label%: %name% %extra%'`) : string
Generate a title describing the class this object is referring to | 534 | | public | getExtends() : string | 535 | | public | getFunctions() : [\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] | 536 | | public | getInterfaces() : array | 537 | | public | hasIgnoreTag(bool $toggle=null) : bool | 538 | | public | isAbstract(bool $toggle=null) : bool | 539 | | public | isInterface(bool $toggle=null) : bool | 540 | | public | isNative(bool $toggle=null) : bool | 541 | | public | isSame(string/object $class) : bool
Check whether this object is referring to given class name or object instance | 542 | | public | setExtends(string $extends) : void | 543 | | public | setFunctions([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity)[] $functions) : void | 544 | | public | setInterfaces(array $implements) : void | 545 | | public | setName(string $name) : void | 546 | 547 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 548 | 549 |
550 | 551 | ### Class: \PHPDocsMD\ClassEntityFactory 552 | 553 | > Class capable of creating ClassEntity objects 554 | 555 | | Visibility | Function | 556 | |:-----------|:---------| 557 | | public | __construct([\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor) : void | 558 | | public | create([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflection) : mixed | 559 | 560 |
561 | 562 | ### Class: \PHPDocsMD\CodeEntity 563 | 564 | > Object describing a piece of code 565 | 566 | | Visibility | Function | 567 | |:-----------|:---------| 568 | | public | getDeprecationMessage() : string | 569 | | public | getDescription() : string | 570 | | public | getExample() : string | 571 | | public | getName() : string | 572 | | public | isDeprecated(bool $toggle=null) : void/bool | 573 | | public | setDeprecationMessage(string $deprecationMessage) : void | 574 | | public | setDescription(string $description) : void | 575 | | public | setExample(string $example) : void | 576 | | public | setName(string $name) : void | 577 | 578 |
579 | 580 | ### Class: \PHPDocsMD\DocInfo 581 | 582 | > Class containing information about a function/class that's being made available via a comment block 583 | 584 | | Visibility | Function | 585 | |:-----------|:---------| 586 | | public | __construct(array $data) : void | 587 | | public | getDeprecationMessage() : string | 588 | | public | getDescription() : string | 589 | | public | getExample() : string | 590 | | public | getParameterInfo(string $name) : array | 591 | | public | getParameters() : array | 592 | | public | getReturnType() : string | 593 | | public | shouldBeIgnored() : bool | 594 | | public | shouldInheritDoc() : bool | 595 | 596 |
597 | 598 | ### Class: \PHPDocsMD\DocInfoExtractor 599 | 600 | > Class that can extract information from a function/class comment 601 | 602 | | Visibility | Function | 603 | |:-----------|:---------| 604 | | public | applyInfoToEntity([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection, [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $docInfo, [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity) $code) : void | 605 | | public | extractInfo([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php)/[\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $reflection) : [\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) | 606 | 607 |
608 | 609 | ### Class: \PHPDocsMD\FunctionEntity 610 | 611 | > Object describing a function 612 | 613 | | Visibility | Function | 614 | |:-----------|:---------| 615 | | public | getClass() : string | 616 | | public | getParams() : [\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] | 617 | | public | getReturnType() : string | 618 | | public | getVisibility() : string | 619 | | public | hasParams() : bool | 620 | | public | isAbstract(bool $toggle=null) : bool | 621 | | public | isReturningNativeClass(bool $toggle=null) : bool | 622 | | public | isStatic(bool $toggle=null) : bool | 623 | | public | setClass(string $class) : void | 624 | | public | setParams([\PHPDocsMD\ParamEntity](#class-phpdocsmdparamentity)[] $params) : void | 625 | | public | setReturnType(string $returnType) : void | 626 | | public | setVisibility(string $visibility) : void | 627 | 628 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 629 | 630 |
631 | 632 | ### Class: \PHPDocsMD\FunctionFinder 633 | 634 | > Find a specific function in a class or an array of classes 635 | 636 | | Visibility | Function | 637 | |:-----------|:---------| 638 | | public | find(string $methodName, string $className) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 639 | | public | findInClasses(string $methodName, array $classes) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 640 | 641 |
642 | 643 | ### Class: \PHPDocsMD\MDTableGenerator 644 | 645 | > Class that can create a markdown-formatted table describing class functions referred to via FunctionEntity objects 646 | 647 | ###### Example 648 | ```php 649 | openTable(); 652 | foreach($classEntity->getFunctions() as $func) { 653 | $generator->addFunc( $func ); 654 | } 655 | echo $generator->getTable(); 656 | ``` 657 | 658 | | Visibility | Function | 659 | |:-----------|:---------| 660 | | public | addFunc([\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) $func) : string
Generates a markdown formatted table row with information about given function. Then adds the row to the table and returns the markdown formatted string. | 661 | | public | appendExamplesToEndOfTable(bool $toggle) : void
All example comments found while generating the table will be appended to the end of the table. Set $toggle to false to prevent this behaviour | 662 | | public | doDeclareAbstraction(bool $toggle) : void
Toggle whether or not methods being abstract (or part of an interface) should be declared as abstract in the table | 663 | | public static | formatExampleComment(string $example) : string
Create a markdown-formatted code view out of an example comment | 664 | | public | getTable() : string | 665 | | public | openTable() : void
Begin generating a new markdown-formatted table | 666 | 667 |
668 | 669 | ### Class: \PHPDocsMD\ParamEntity 670 | 671 | > Object describing a function parameter 672 | 673 | | Visibility | Function | 674 | |:-----------|:---------| 675 | | public | getDefault() : boolean | 676 | | public | getNativeClassType() : string/null | 677 | | public | getType() : string | 678 | | public | setDefault(boolean $default) : void | 679 | | public | setType(string $type) : void | 680 | 681 | *This class extends [\PHPDocsMD\CodeEntity](#class-phpdocsmdcodeentity)* 682 | 683 |
684 | 685 | ### Class: \PHPDocsMD\Reflector 686 | 687 | > Class that can compute ClassEntity objects out of real classes 688 | 689 | | Visibility | Function | 690 | |:-----------|:---------| 691 | | public | __construct(string $className, [\PHPDocsMD\FunctionFinder](#class-phpdocsmdfunctionfinder) $functionFinder=null, [\PHPDocsMD\DocInfoExtractor](#class-phpdocsmddocinfoextractor) $docInfoExtractor=null, [\PHPDocsMD\UseInspector](#class-phpdocsmduseinspector) $useInspector=null, [\PHPDocsMD\ClassEntityFactory](#class-phpdocsmdclassentityfactory) $classEntityFactory=null) : void | 692 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 693 | | public static | getParamType([\ReflectionParameter](http://php.net/manual/en/class.reflectionparameter.php) $refParam) : string
Tries to find out if the type of the given parameter. Will return empty string if not possible. | 694 | | protected | createFunctionEntity([\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class, array $useStatements) : bool/[\PHPDocsMD\FunctionEntity](#class-phpdocsmdfunctionentity) | 695 | | protected | shouldIgnoreFunction([\PHPDocsMD\DocInfo](#class-phpdocsmddocinfo) $info, [\ReflectionMethod](http://php.net/manual/en/class.reflectionmethod.php) $method, [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) $class) : bool | 696 | ###### Examples of Reflector::getParamType() 697 | ```php 698 | getMethods() as $method ) { 701 | foreach($method->getParameters() as $param) { 702 | $name = $param->getName(); 703 | $type = Reflector::getParamType($param); 704 | printf("%s = %s\n", $name, $type); 705 | } 706 | } 707 | ``` 708 | 709 | *This class implements [\PHPDocsMD\ReflectorInterface](#interface-phpdocsmdreflectorinterface)* 710 | 711 |
712 | 713 | ### Interface: \PHPDocsMD\ReflectorInterface 714 | 715 | > Interface for classes that can compute ClassEntity objects 716 | 717 | | Visibility | Function | 718 | |:-----------|:---------| 719 | | public | getClassEntity() : [\PHPDocsMD\ClassEntity](#class-phpdocsmdclassentity) | 720 | 721 |
722 | 723 | ### Class: \PHPDocsMD\UseInspector 724 | 725 | > Class that can extract all use statements in a file 726 | 727 | | Visibility | Function | 728 | |:-----------|:---------| 729 | | public | getUseStatements([\ReflectionClass](http://php.net/manual/en/class.reflectionclass.php) $reflectionClass) : array | 730 | | public | getUseStatementsInFile(string $filePath) : array | 731 | | public | getUseStatementsInString(string $content) : string[] | 732 | 733 |
734 | 735 | ### Class: \PHPDocsMD\Utils 736 | 737 | | Visibility | Function | 738 | |:-----------|:---------| 739 | | public static | getClassBaseName(string $fullClassName) : string | 740 | | public static | isClassReference(string $typeDeclaration) : bool | 741 | | public static | isNativeClassReference(mixed $typeDeclaration) : bool | 742 | | public static | sanitizeClassName(string $name) : string | 743 | | public static | sanitizeDeclaration(string $typeDeclaration, string $currentNameSpace, string $delimiter=`'|'`) : string | 744 | 745 |
746 | 747 | ### Class: \PHPDocsMD\Console\CLI 748 | 749 | > Command line interface used to extract markdown-formatted documentation from classes 750 | 751 | | Visibility | Function | 752 | |:-----------|:---------| 753 | | public | __construct() : void | 754 | | public | run(\Symfony\Component\Console\Input\InputInterface $input=null, \Symfony\Component\Console\Output\OutputInterface $output=null) : int | 755 | 756 | *This class extends \Symfony\Component\Console\Application* 757 | 758 |
759 | 760 | ### Class: \PHPDocsMD\Console\PHPDocsMDCommand 761 | 762 | > Console command used to extract markdown-formatted documentation from classes 763 | 764 | | Visibility | Function | 765 | |:-----------|:---------| 766 | | public | extractClassNameFromLine(string $type, string $line) : string | 767 | | protected | configure() : void | 768 | | protected | execute(\Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output) : int/null | 769 | 770 | *This class extends \Symfony\Component\Console\Command\Command* 771 | 772 | -------------------------------------------------------------------------------- /src/PHPDocsMD/ClassEntity.php: -------------------------------------------------------------------------------- 1 | abstract; 64 | } else { 65 | return $this->abstract = (bool)$toggle; 66 | } 67 | } 68 | 69 | /** 70 | * @param bool $toggle 71 | * @return bool 72 | */ 73 | public function hasIgnoreTag($toggle=null) 74 | { 75 | if( $toggle === null ) { 76 | return $this->hasIgnoreTag; 77 | } else { 78 | return $this->hasIgnoreTag = (bool)$toggle; 79 | } 80 | } 81 | 82 | /** 83 | * @param bool $toggle 84 | * @return bool 85 | */ 86 | public function hasInternalTag($toggle = null) 87 | { 88 | if ($toggle === null) { 89 | return $this->hasInternalTag; 90 | } else { 91 | return $this->hasInternalTag = (bool)$toggle; 92 | } 93 | } 94 | 95 | /** 96 | * @param bool $toggle 97 | * @return bool 98 | */ 99 | public function isInterface($toggle=null) 100 | { 101 | if( $toggle === null ) { 102 | return $this->isInterface; 103 | } else { 104 | return $this->isInterface = (bool)$toggle; 105 | } 106 | } 107 | 108 | /** 109 | * @param bool $toggle 110 | * @return bool 111 | */ 112 | public function isNative($toggle=null) 113 | { 114 | if( $toggle === null ) { 115 | return $this->isNative; 116 | } else { 117 | return $this->isNative = (bool)$toggle; 118 | } 119 | } 120 | 121 | /** 122 | * @param string $extends 123 | */ 124 | public function setExtends($extends) 125 | { 126 | $this->extends = Utils::sanitizeClassName($extends); 127 | } 128 | 129 | /** 130 | * @return string 131 | */ 132 | public function getExtends() 133 | { 134 | return $this->extends; 135 | } 136 | 137 | /** 138 | * @param \PHPDocsMD\FunctionEntity[] $functions 139 | */ 140 | public function setFunctions(array $functions) 141 | { 142 | $this->functions = $functions; 143 | } 144 | 145 | /** 146 | * @param array $see 147 | */ 148 | public function setSee(array $see) 149 | { 150 | $this->see = []; 151 | foreach($see as $i) { 152 | $this->see[] = $i; 153 | } 154 | } 155 | 156 | /** 157 | * @param array $implements 158 | */ 159 | public function setInterfaces(array $implements) 160 | { 161 | $this->interfaces = []; 162 | foreach($implements as $interface) { 163 | $this->interfaces[] = Utils::sanitizeClassName($interface); 164 | } 165 | } 166 | 167 | /** 168 | * @return array 169 | */ 170 | public function getInterfaces() 171 | { 172 | return $this->interfaces; 173 | } 174 | 175 | /** 176 | * @return array 177 | */ 178 | public function getSee() 179 | { 180 | return $this->see; 181 | } 182 | 183 | /** 184 | * @return \PHPDocsMD\FunctionEntity[] 185 | */ 186 | public function getFunctions() 187 | { 188 | return $this->functions; 189 | } 190 | 191 | /** 192 | * @param string $name 193 | */ 194 | function setName($name) 195 | { 196 | parent::setName(Utils::sanitizeClassName($name)); 197 | } 198 | 199 | /** 200 | * Check whether this object is referring to given class name or object instance 201 | * @param string|object $class 202 | * @return bool 203 | */ 204 | function isSame($class) 205 | { 206 | $className = is_object($class) ? get_class($class) : $class; 207 | return Utils::sanitizeClassName($className) == $this->getName(); 208 | } 209 | 210 | /** 211 | * Generate a title describing the class this object is referring to 212 | * @param string $format 213 | * @return string 214 | */ 215 | function generateTitle($format='%label%: %name% %extra%') 216 | { 217 | $translate = [ 218 | '%label%' => $this->isInterface() ? 'Interface' : 'Class', 219 | '%name%' => substr_count($this->getName(), '\\') == 1 ? substr($this->getName(), 1) : $this->getName(), 220 | '%extra%' => '' 221 | ]; 222 | 223 | if( strpos($format, '%label%') === false ) { 224 | if( $this->isInterface() ) 225 | $translate['%extra%'] = '(interface)'; 226 | elseif( $this->isAbstract() ) 227 | $translate['%extra%'] = '(abstract)'; 228 | } else { 229 | $translate['%extra%'] = $this->isAbstract() && !$this->isInterface() ? '(abstract)' : ''; 230 | } 231 | 232 | return trim(strtr($format, $translate)); 233 | } 234 | 235 | /** 236 | * Generates an anchor link out of the generated title (see generateTitle) 237 | * @return string 238 | */ 239 | function generateAnchor() 240 | { 241 | $title = $this->generateTitle(); 242 | return strtolower(str_replace([':', ' ', '\\', '(', ')'], ['', '-', '', '', ''], $title)); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/PHPDocsMD/ClassEntityFactory.php: -------------------------------------------------------------------------------- 1 | docInfoExtractor = $docInfoExtractor; 21 | } 22 | 23 | public function create(\ReflectionClass $reflection) 24 | { 25 | $class = new ClassEntity(); 26 | $docInfo = $this->docInfoExtractor->extractInfo($reflection); 27 | $this->docInfoExtractor->applyInfoToEntity($reflection, $docInfo, $class); 28 | $class->isInterface($reflection->isInterface()); 29 | $class->isAbstract($reflection->isAbstract()); 30 | $class->setInterfaces(array_keys($reflection->getInterfaces())); 31 | $class->hasIgnoreTag($docInfo->shouldBeIgnored()); 32 | $class->hasInternalTag($docInfo->isInternal()); 33 | 34 | if ($reflection->getParentClass()) { 35 | $class->setExtends($reflection->getParentClass()->getName()); 36 | } 37 | 38 | return $class; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/PHPDocsMD/CodeEntity.php: -------------------------------------------------------------------------------- 1 | isDeprecated; 50 | } else { 51 | return $this->isDeprecated = (bool)$toggle; 52 | } 53 | } 54 | 55 | /** 56 | * @param bool $toggle 57 | * @return bool|null 58 | */ 59 | public function isInternal($toggle=null) 60 | { 61 | if ($toggle === null) { 62 | return $this->isInternal; 63 | } else { 64 | return $this->isInternal = (bool)$toggle; 65 | } 66 | } 67 | 68 | /** 69 | * @param string $description 70 | */ 71 | public function setDescription($description) 72 | { 73 | $this->description = $description; 74 | } 75 | 76 | /** 77 | * @return string 78 | */ 79 | public function getDescription() 80 | { 81 | return $this->description; 82 | } 83 | 84 | /** 85 | * @param string $name 86 | */ 87 | public function setName($name) 88 | { 89 | $this->name = $name; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function getName() 96 | { 97 | return $this->name; 98 | } 99 | 100 | /** 101 | * @param string $deprecationMessage 102 | */ 103 | public function setDeprecationMessage($deprecationMessage) 104 | { 105 | $this->deprecationMessage = $deprecationMessage; 106 | } 107 | 108 | /** 109 | * @return string 110 | */ 111 | public function getDeprecationMessage() 112 | { 113 | return $this->deprecationMessage; 114 | } 115 | 116 | /** 117 | * @param string $example 118 | */ 119 | public function setExample($example) 120 | { 121 | $this->example = $example; 122 | } 123 | 124 | /** 125 | * @return string 126 | */ 127 | public function getExample() 128 | { 129 | return $this->example; 130 | } 131 | } -------------------------------------------------------------------------------- /src/PHPDocsMD/Console/CLI.php: -------------------------------------------------------------------------------- 1 | version); 19 | } 20 | 21 | /** 22 | * @param \Symfony\Component\Console\Input\InputInterface $input 23 | * @param \Symfony\Component\Console\Input\OutputInterface $output 24 | * @return int 25 | */ 26 | public function run(InputInterface $input=null, OutputInterface $output=null) 27 | { 28 | $this->add(new PHPDocsMDCommand()); 29 | return parent::run($input, $output); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PHPDocsMD/Console/PHPDocsMDCommand.php: -------------------------------------------------------------------------------- 1 | memory[$name]) ) { 50 | $reflector = new Reflector($name); 51 | if ( ! empty($this->visibilityFilter)) { 52 | $reflector->setVisibilityFilter($this->visibilityFilter); 53 | } 54 | if ( ! empty($this->methodRegex)) { 55 | $reflector->setMethodRegex($this->methodRegex); 56 | } 57 | $this->memory[$name] = $reflector->getClassEntity(); 58 | } 59 | return $this->memory[$name]; 60 | } 61 | 62 | protected function configure() 63 | { 64 | $this 65 | ->setName('generate') 66 | ->setDescription('Get docs for given class/source directory)') 67 | ->addArgument( 68 | self::ARG_CLASS, 69 | InputArgument::REQUIRED, 70 | 'Class or source directory' 71 | ) 72 | ->addOption( 73 | self::OPT_BOOTSTRAP, 74 | 'b', 75 | InputOption::VALUE_REQUIRED, 76 | 'File to be included before generating documentation' 77 | ) 78 | ->addOption( 79 | self::OPT_IGNORE, 80 | 'i', 81 | InputOption::VALUE_REQUIRED, 82 | 'Directories to ignore', 83 | '' 84 | ) 85 | ->addOption( 86 | self::OPT_VISIBILITY, 87 | null, 88 | InputOption::VALUE_OPTIONAL, 89 | 'The visibility of the methods to import, a comma-separated list.', 90 | '' 91 | ) 92 | ->addOption( 93 | self::OPT_METHOD_REGEX, 94 | null, 95 | InputOption::VALUE_OPTIONAL, 96 | 'The full regular expression methods should match to be included in the output.', 97 | '' 98 | ) 99 | ->addOption( 100 | self::OPT_TABLE_GENERATOR, 101 | null, 102 | InputOption::VALUE_OPTIONAL, 103 | 'The slug of a supported table generator class or a fully qualified TableGenerator interface implementation class name.', 104 | 'default' 105 | ) 106 | ->addOption( 107 | self::OPT_SEE, 108 | null, 109 | InputOption::VALUE_NONE, 110 | 'Include @see in generated markdown' 111 | ) 112 | ->addOption( 113 | self::OPT_NO_INTERNAL, 114 | null, 115 | InputOption::VALUE_NONE, 116 | 'Ignore entities marked @internal' 117 | ); 118 | } 119 | 120 | /** 121 | * @param InputInterface $input 122 | * @param OutputInterface $output 123 | * @return int|null 124 | * @throws \InvalidArgumentException 125 | */ 126 | protected function execute(InputInterface $input, OutputInterface $output) 127 | { 128 | 129 | $classes = $input->getArgument(self::ARG_CLASS); 130 | $bootstrap = $input->getOption(self::OPT_BOOTSTRAP); 131 | $ignore = explode(',', $input->getOption(self::OPT_IGNORE)); 132 | $this->visibilityFilter = empty($input->getOption(self::OPT_VISIBILITY)) 133 | ? ['public', 'protected', 'abstract', 'final'] 134 | : array_map('trim', preg_split('/\\s*,\\s*/', $input->getOption(self::OPT_VISIBILITY))); 135 | $this->methodRegex = $input->getOption(self::OPT_METHOD_REGEX) ?: false; 136 | $includeSee = $input->getOption(self::OPT_SEE); 137 | $noInternal = $input->getOption(self::OPT_NO_INTERNAL); 138 | $requestingOneClass = false; 139 | 140 | if( $bootstrap ) { 141 | require_once strpos($bootstrap,'/') === 0 ? $bootstrap : getcwd().'/'.$bootstrap; 142 | } 143 | 144 | $classCollection = []; 145 | if( strpos($classes, ',') !== false ) { 146 | foreach(explode(',', $classes) as $class) { 147 | if( class_exists($class) || interface_exists($class) || trait_exists($class) ) 148 | $classCollection[0][] = $class; 149 | } 150 | } 151 | elseif( class_exists($classes) || interface_exists($classes) || trait_exists($classes) ) { 152 | $classCollection[] = array($classes); 153 | $requestingOneClass = true; 154 | } elseif( is_dir($classes) ) { 155 | $classCollection = $this->findClassesInDir($classes, [], $ignore); 156 | } else { 157 | throw new \InvalidArgumentException('Given input is neither a class nor a source directory'); 158 | } 159 | 160 | $tableGeneratorSlug = $input->getOption(self::OPT_TABLE_GENERATOR); 161 | $tableGenerator = $this->buildTableGenerator($tableGeneratorSlug); 162 | 163 | $tableOfContent = []; 164 | $body = []; 165 | $classLinks = []; 166 | 167 | foreach($classCollection as $ns => $classes) { 168 | foreach($classes as $className) { 169 | $class = $this->getClassEntity($className); 170 | 171 | if ($class->hasIgnoreTag() 172 | || ($class->hasInternalTag() && $noInternal)) { 173 | continue; 174 | } 175 | 176 | // Add to tbl of contents 177 | $tableOfContent[] = sprintf('- [%s](#%s)', $class->generateTitle('%name% %extra%'), $class->generateAnchor()); 178 | 179 | $classLinks[$class->getName()] = '#'.$class->generateAnchor(); 180 | 181 | // generate function table 182 | $tableGenerator->openTable(); 183 | $tableGenerator->doDeclareAbstraction(!$class->isInterface()); 184 | foreach($class->getFunctions() as $func) { 185 | if ($func->isInternal() && $noInternal) { 186 | continue; 187 | } 188 | if ($func->isReturningNativeClass()) { 189 | $classLinks[$func->getReturnType()] = 'http://php.net/manual/en/class.'. 190 | strtolower(str_replace(array('[]', '\\'), '', $func->getReturnType())). 191 | '.php'; 192 | } 193 | foreach($func->getParams() as $param) { 194 | if ($param->getNativeClassType()) { 195 | $classLinks[$param->getNativeClassType()] = 'http://php.net/manual/en/class.'. 196 | strtolower(str_replace(array('[]', '\\'), '', $param->getNativeClassType())). 197 | '.php'; 198 | } 199 | } 200 | $tableGenerator->addFunc($func, $includeSee); 201 | } 202 | 203 | $docs = ($requestingOneClass ? '':'
'.PHP_EOL); 204 | 205 | if( $class->isDeprecated() ) { 206 | $docs .= '### '.$class->generateTitle().''.PHP_EOL.PHP_EOL. 207 | '> **DEPRECATED** '.$class->getDeprecationMessage().PHP_EOL.PHP_EOL; 208 | } 209 | else { 210 | $docs .= '### '.$class->generateTitle().PHP_EOL.PHP_EOL; 211 | if( $class->getDescription() ) 212 | $docs .= '> '.$class->getDescription().PHP_EOL.PHP_EOL; 213 | } 214 | 215 | if ($includeSee && $seeArray = $class->getSee()) { 216 | foreach ($seeArray as $see) { 217 | $docs .= 'See ' . $see . '
' . PHP_EOL; 218 | } 219 | $docs .= PHP_EOL; 220 | } 221 | 222 | if( $example = $class->getExample() ) { 223 | $docs .= '###### Example' . PHP_EOL . MDTableGenerator::formatExampleComment($example) .PHP_EOL.PHP_EOL; 224 | } 225 | 226 | $docs .= $tableGenerator->getTable().PHP_EOL; 227 | 228 | if( $class->getExtends() ) { 229 | $link = $class->getExtends(); 230 | if( $anchor = $this->getAnchorFromClassCollection($classCollection, $class->getExtends()) ) { 231 | $link = sprintf('[%s](#%s)', $link, $anchor); 232 | } 233 | 234 | $docs .= PHP_EOL.'*This class extends '.$link.'*'.PHP_EOL; 235 | } 236 | 237 | if( $interfaces = $class->getInterfaces() ) { 238 | $interfaceNames = []; 239 | foreach($interfaces as $interface) { 240 | $anchor = $this->getAnchorFromClassCollection($classCollection, $interface); 241 | $interfaceNames[] = $anchor ? sprintf('[%s](#%s)', $interface, $anchor) : $interface; 242 | } 243 | $docs .= PHP_EOL.'*This class implements '.implode(', ', $interfaceNames).'*'.PHP_EOL; 244 | } 245 | 246 | $body[] = $docs; 247 | } 248 | } 249 | 250 | if( empty($tableOfContent) ) { 251 | throw new \InvalidArgumentException('No classes found'); 252 | } elseif( !$requestingOneClass ) { 253 | $output->writeln('## Table of contents'.PHP_EOL); 254 | $output->writeln(implode(PHP_EOL, $tableOfContent)); 255 | } 256 | 257 | // Convert references to classes into links 258 | asort($classLinks); 259 | $classLinks = array_reverse($classLinks, true); 260 | $docString = implode(PHP_EOL, $body); 261 | foreach($classLinks as $className => $url) { 262 | $link = sprintf('[%s](%s)', $className, $url); 263 | $find = array(''.$className, '/'.$className); 264 | $replace = array(''.$link, '/'.$link); 265 | $docString = str_replace($find, $replace, $docString); 266 | } 267 | 268 | $output->writeln(PHP_EOL.$docString); 269 | 270 | return 0; 271 | } 272 | 273 | /** 274 | * @param $coll 275 | * @param $find 276 | * @return bool|string 277 | */ 278 | private function getAnchorFromClassCollection($coll, $find) 279 | { 280 | foreach($coll as $ns => $classes) { 281 | foreach($classes as $className) { 282 | if( $className == $find ) { 283 | return $this->getClassEntity($className)->generateAnchor(); 284 | } 285 | } 286 | } 287 | return false; 288 | } 289 | 290 | /** 291 | * @param $file 292 | * @return array 293 | */ 294 | private function findClassInFile($file) 295 | { 296 | $ns = ''; 297 | $class = false; 298 | foreach(explode(PHP_EOL, file_get_contents($file)) as $line) { 299 | if ( strpos($line, '*') === false ) { 300 | if( strpos($line, 'namespace') !== false ) { 301 | $ns = trim(current(array_slice(explode('namespace', $line), 1)), '; '); 302 | $ns = Utils::sanitizeClassName($ns); 303 | } elseif( strpos($line, 'class') !== false ) { 304 | $class = $this->extractClassNameFromLine('class', $line); 305 | break; 306 | } elseif( strpos($line, 'interface') !== false ) { 307 | $class = $this->extractClassNameFromLine('interface', $line); 308 | break; 309 | } 310 | } 311 | } 312 | return $class ? array($ns, $ns .'\\'. $class) : array(false, false); 313 | } 314 | 315 | /** 316 | * @param string $type 317 | * @param string $line 318 | * @return string 319 | */ 320 | function extractClassNameFromLine($type, $line) 321 | { 322 | $class = trim(current(array_slice(explode($type, $line), 1)), '; '); 323 | return trim(current(explode(' ', $class))); 324 | } 325 | 326 | /** 327 | * @param $dir 328 | * @param array $collection 329 | * @param array $ignores 330 | * @return array 331 | */ 332 | private function findClassesInDir($dir, $collection=[], $ignores=[]) 333 | { 334 | foreach(new \FilesystemIterator($dir) as $f) { 335 | /** @var \SplFileInfo $f */ 336 | if( $f->isFile() && !$f->isLink() ) { 337 | list($ns, $className) = $this->findClassInFile($f->getRealPath()); 338 | if( $className && (class_exists($className, true) || interface_exists($className) || trait_exists($className)) ) { 339 | $collection[$ns][] = $className; 340 | } 341 | } elseif( $f->isDir() && !$f->isLink() && !$this->shouldIgnoreDirectory($f->getFilename(), $ignores) ) { 342 | $collection = $this->findClassesInDir($f->getRealPath(), $collection); 343 | } 344 | } 345 | ksort($collection); 346 | return $collection; 347 | } 348 | 349 | /** 350 | * @param $dirName 351 | * @param $ignores 352 | * @return bool 353 | */ 354 | private function shouldIgnoreDirectory($dirName, $ignores) { 355 | foreach($ignores as $dir) { 356 | $dir = trim($dir); 357 | if( !empty($dir) && substr($dirName, -1 * strlen($dir)) == $dir ) { 358 | return true; 359 | } 360 | } 361 | return false; 362 | } 363 | 364 | protected function buildTableGenerator($tableGeneratorSlug = 'default') 365 | { 366 | if (class_exists($tableGeneratorSlug)) { 367 | if (!in_array(TableGenerator::class, class_implements($tableGeneratorSlug), true)) { 368 | throw new \InvalidArgumentException('The table generator class should implement the ' . 369 | TableGenerator::class . ' interface.'); 370 | } 371 | 372 | return new $tableGeneratorSlug(); 373 | } 374 | 375 | $map = [ 376 | 'default' => MDTableGenerator::class, 377 | ]; 378 | 379 | $class = isset($map[$tableGeneratorSlug]) ? $map[$tableGeneratorSlug] : $map['default']; 380 | 381 | return new $class; 382 | } 383 | 384 | } 385 | -------------------------------------------------------------------------------- /src/PHPDocsMD/DocInfo.php: -------------------------------------------------------------------------------- 1 | data = array_merge([ 24 | 'return' => '', 25 | 'params' => [], 26 | 'description' => '', 27 | 'example' => false, 28 | 'deprecated' => false, 29 | 'see' => [] 30 | ], $data); 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function getReturnType() 37 | { 38 | return $this->data['return']; 39 | } 40 | 41 | /** 42 | * @return array 43 | */ 44 | public function getParameters() 45 | { 46 | return $this->data['params']; 47 | } 48 | 49 | /** 50 | * @param string $name 51 | * @return array 52 | */ 53 | public function getParameterInfo($name) 54 | { 55 | if (isset($this->data['params'][$name])) { 56 | return $this->data['params'][$name]; 57 | } 58 | return []; 59 | } 60 | 61 | /** 62 | * @return string 63 | */ 64 | public function getExample() 65 | { 66 | return $this->data['example']; 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | public function getDescription() 73 | { 74 | return $this->data['description']; 75 | } 76 | 77 | /** 78 | * @return string 79 | */ 80 | public function getDeprecationMessage() 81 | { 82 | return $this->data['deprecated']; 83 | } 84 | 85 | /** 86 | * @return array 87 | */ 88 | public function getSee() 89 | { 90 | return $this->data['see']; 91 | } 92 | 93 | /** 94 | * @return bool 95 | */ 96 | public function shouldInheritDoc() 97 | { 98 | return isset($this->data['inheritDoc']) || isset($this->data['inheritdoc']); 99 | } 100 | 101 | /** 102 | * @return bool 103 | */ 104 | public function shouldBeIgnored() 105 | { 106 | return isset($this->data['ignore']); 107 | } 108 | 109 | /** 110 | * @return bool 111 | */ 112 | public function isInternal() 113 | { 114 | return isset($this->data['internal']); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/PHPDocsMD/DocInfoExtractor.php: -------------------------------------------------------------------------------- 1 | getCleanDocComment($reflection); 17 | $data = $this->extractInfoFromComment($comment, $reflection); 18 | return new DocInfo($data); 19 | } 20 | 21 | /** 22 | * @param \ReflectionClass|\ReflectionMethod $reflection 23 | * @param DocInfo $docInfo 24 | * @param CodeEntity $code 25 | */ 26 | public function applyInfoToEntity($reflection, DocInfo $docInfo, CodeEntity $code) 27 | { 28 | $code->setName($reflection->getName()); 29 | $code->setDescription($docInfo->getDescription()); 30 | $code->setExample($docInfo->getExample()); 31 | $code->setSee($docInfo->getSee()); 32 | $code->isInternal($docInfo->isInternal()); 33 | 34 | if ($docInfo->getDeprecationMessage()) { 35 | $code->isDeprecated(true); 36 | $code->setDeprecationMessage($docInfo->getDeprecationMessage()); 37 | } 38 | } 39 | 40 | /** 41 | * @param \ReflectionClass $reflection 42 | * @return string 43 | */ 44 | private function getCleanDocComment($reflection) 45 | { 46 | $comment = str_replace(['/*', '*/'], '', $reflection->getDocComment()); 47 | return trim(trim(preg_replace('/([\s|^]\*\s)/', '', $comment)), '*'); 48 | } 49 | 50 | /** 51 | * @param string $comment 52 | * @param string $current_tag 53 | * @param \ReflectionMethod|\ReflectionClass $reflection 54 | * @return array 55 | */ 56 | private function extractInfoFromComment($comment, $reflection, $current_tag='description') 57 | { 58 | $currentNamespace = $this->getNameSpace($reflection); 59 | $tags = [$current_tag=>'']; 60 | 61 | foreach(explode(PHP_EOL, $comment) as $line) { 62 | 63 | if( $current_tag != 'example' ) 64 | $line = trim($line); 65 | 66 | $words = $this->getWordsFromLine($line); 67 | if( empty($words) ) 68 | continue; 69 | 70 | if( strpos($words[0], '@') === false ) { 71 | // Append to tag 72 | $joinWith = $current_tag == 'example' ? PHP_EOL : ' '; 73 | $tags[$current_tag] .= $joinWith . $line; 74 | } 75 | elseif( $words[0] == '@param' ) { 76 | // Get parameter declaration 77 | if( $paramData = $this->figureOutParamDeclaration($words, $currentNamespace) ) { 78 | list($name, $data) = $paramData; 79 | $tags['params'][$name] = $data; 80 | } 81 | } 82 | elseif( $words[0] == '@see' ) { 83 | $tags['see'][] = $this->figureOutSeeDeclaration($words); 84 | } 85 | else { 86 | // Start new tag 87 | $current_tag = substr($words[0], 1); 88 | array_splice($words, 0 ,1); 89 | if( empty($tags[$current_tag]) ) { 90 | $tags[$current_tag] = ''; 91 | } 92 | $tags[$current_tag] .= trim(join(' ', $words)); 93 | } 94 | } 95 | 96 | foreach($tags as $name => $val) { 97 | if( is_array($val) ) { 98 | foreach($val as $subName=>$subVal) { 99 | if( is_string($subVal) ) 100 | $tags[$name][$subName] = trim($subVal); 101 | } 102 | } else { 103 | $tags[$name] = trim($val); 104 | } 105 | } 106 | 107 | return $tags; 108 | } 109 | 110 | /** 111 | * @param \ReflectionClass|\ReflectionMethod $reflection 112 | * @return string 113 | */ 114 | private function getNameSpace($reflection) 115 | { 116 | if ($reflection instanceof \ReflectionClass) { 117 | return $reflection->getNamespaceName(); 118 | } else { 119 | return $reflection->getDeclaringClass()->getNamespaceName(); 120 | } 121 | } 122 | 123 | /** 124 | * @param $line 125 | * @return array 126 | */ 127 | private function getWordsFromLine($line) 128 | { 129 | $words = []; 130 | foreach(explode(' ', trim($line)) as $w) { 131 | if( !empty($w) ) { 132 | $words[] = $w; 133 | } 134 | } 135 | return $words; 136 | } 137 | 138 | /** 139 | * @param $words 140 | * @param $currentNameSpace 141 | * @return array|bool 142 | */ 143 | private function figureOutParamDeclaration($words, $currentNameSpace) 144 | { 145 | $description = ''; 146 | $type = ''; 147 | $name = ''; 148 | 149 | if (isset($words[1]) && strpos($words[1], '$') === 0) { 150 | $name = $words[1]; 151 | $type = 'mixed'; 152 | array_splice($words, 0, 2); 153 | } elseif (isset($words[2])) { 154 | $name = $words[2]; 155 | $type = $words[1]; 156 | array_splice($words, 0, 3); 157 | } 158 | 159 | if (!empty($name)) { 160 | $name = current(explode('=', $name)); 161 | if( count($words) > 1 ) { 162 | $description = join(' ', $words); 163 | } 164 | 165 | $type = Utils::sanitizeDeclaration($type, $currentNameSpace); 166 | 167 | $data = [ 168 | 'description' => $description, 169 | 'name' => $name, 170 | 'type' => $type, 171 | 'default' => false 172 | ]; 173 | 174 | return [$name, $data]; 175 | } 176 | 177 | return false; 178 | } 179 | 180 | /** 181 | * @param $words 182 | * @return array|false 183 | */ 184 | private function figureOutSeeDeclaration($words) 185 | { 186 | array_shift($words); 187 | 188 | if (!$words) { 189 | $see = false; 190 | } elseif (preg_match('#^http://|^https://#', $words[0])) { 191 | $see = count($words) > 1 192 | ? '[' . implode(' ', array_slice($words, 1)) . '](' . $words[0] . ')' 193 | : '<' . $words[0] . '>'; 194 | } else { 195 | $see = implode(' ', $words); 196 | } 197 | 198 | return $see; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/PHPDocsMD/FunctionEntity.php: -------------------------------------------------------------------------------- 1 | isStatic; 58 | } else { 59 | return $this->isStatic = (bool)$toggle; 60 | } 61 | } 62 | 63 | /** 64 | * @param bool $toggle 65 | */ 66 | public function isAbstract($toggle=null) 67 | { 68 | if ( $toggle === null ) { 69 | return $this->abstract; 70 | } else { 71 | return $this->abstract = (bool)$toggle; 72 | } 73 | } 74 | 75 | /** 76 | * @param bool $toggle 77 | */ 78 | public function isReturningNativeClass($toggle=null) 79 | { 80 | if ( $toggle === null ) { 81 | return $this->isReturningNativeClass; 82 | } else { 83 | return $this->isReturningNativeClass = (bool)$toggle; 84 | } 85 | } 86 | 87 | /** 88 | * @return bool 89 | */ 90 | public function hasParams() 91 | { 92 | return !empty($this->params); 93 | } 94 | 95 | /** 96 | * @param \PHPDocsMD\ParamEntity[] $params 97 | */ 98 | public function setParams(array $params) 99 | { 100 | $this->params = $params; 101 | } 102 | 103 | /** 104 | * @return \PHPDocsMD\ParamEntity[] 105 | */ 106 | public function getParams() 107 | { 108 | return $this->params; 109 | } 110 | 111 | /** 112 | * @param string $returnType 113 | */ 114 | public function setReturnType($returnType) 115 | { 116 | $this->returnType = $returnType; 117 | } 118 | 119 | /** 120 | * @return string 121 | */ 122 | public function getReturnType() 123 | { 124 | return $this->returnType; 125 | } 126 | 127 | /** 128 | * @param string $visibility 129 | */ 130 | public function setVisibility($visibility) 131 | { 132 | $this->visibility = $visibility; 133 | } 134 | 135 | /** 136 | * @return string 137 | */ 138 | public function getVisibility() 139 | { 140 | return $this->visibility; 141 | } 142 | 143 | /** 144 | * @param string $class 145 | */ 146 | public function setClass($class) 147 | { 148 | $this->class = $class; 149 | } 150 | 151 | /** 152 | * @return string 153 | */ 154 | public function getClass() 155 | { 156 | return $this->class; 157 | } 158 | 159 | /** 160 | * @param array $see 161 | */ 162 | public function setSee(array $see) 163 | { 164 | $this->see = $see; 165 | } 166 | 167 | /** 168 | * @return array 169 | */ 170 | public function getSee() 171 | { 172 | return $this->see; 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /src/PHPDocsMD/FunctionFinder.php: -------------------------------------------------------------------------------- 1 | find($methodName, $className); 24 | if (false !== $function) { 25 | return $function; 26 | } 27 | } 28 | return false; 29 | } 30 | 31 | /** 32 | * @param string $methodName 33 | * @param string $className 34 | * @return bool|FunctionEntity 35 | */ 36 | public function find($methodName, $className) 37 | { 38 | if ($className) { 39 | $classEntity = $this->loadClassEntity($className); 40 | $functions = $classEntity->getFunctions(); 41 | foreach($functions as $function) { 42 | if($function->getName() == $methodName) { 43 | return $function; 44 | } 45 | } 46 | if($classEntity->getExtends()) { 47 | return $this->find($methodName, $classEntity->getExtends()); 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | /** 54 | * @param $className 55 | * @return ClassEntity 56 | */ 57 | private function loadClassEntity($className) 58 | { 59 | if (empty($this->cache[$className])) { 60 | $reflector = new Reflector($className, $this); 61 | $this->cache[$className] = $reflector->getClassEntity(); 62 | } 63 | 64 | return $this->cache[$className]; 65 | } 66 | } -------------------------------------------------------------------------------- /src/PHPDocsMD/MDTableGenerator.php: -------------------------------------------------------------------------------- 1 | 11 | * openTable(); 14 | * foreach($classEntity->getFunctions() as $func) { 15 | * $generator->addFunc( $func ); 16 | * } 17 | * echo $generator->getTable(); 18 | * 19 | * 20 | * @package PHPDocsMD 21 | */ 22 | class MDTableGenerator implements TableGenerator 23 | { 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $fullClassName = ''; 29 | 30 | /** 31 | * @var string 32 | */ 33 | private $markdown = ''; 34 | 35 | /** 36 | * @var array 37 | */ 38 | private $examples = []; 39 | 40 | /** 41 | * @var bool 42 | */ 43 | private $appendExamples = true; 44 | 45 | /** 46 | * @var bool 47 | */ 48 | private $declareAbstraction = true; 49 | 50 | /** 51 | * @param $example 52 | * @return mixed 53 | */ 54 | private static function stripCodeTags($example) 55 | { 56 | if (strpos($example, '', $example), -2); 58 | $example = current($parts); 59 | $parts = array_slice(explode('', $example), 1); 60 | $example = current($parts); 61 | } 62 | return $example; 63 | } 64 | 65 | /** 66 | * All example comments found while generating the table will be 67 | * appended to the end of the table. Set $toggle to false to 68 | * prevent this behaviour 69 | * 70 | * @param bool $toggle 71 | */ 72 | function appendExamplesToEndOfTable($toggle) 73 | { 74 | $this->appendExamples = (bool)$toggle; 75 | } 76 | 77 | /** 78 | * Begin generating a new markdown-formatted table 79 | */ 80 | function openTable() 81 | { 82 | $this->examples = []; 83 | $this->markdown = ''; // Clear table 84 | $this->declareAbstraction = true; 85 | $this->add('| Visibility | Function |'); 86 | $this->add('|:-----------|:---------|'); 87 | } 88 | 89 | /** 90 | * Toggle whether or not methods being abstract (or part of an interface) 91 | * should be declared as abstract in the table 92 | * @param bool $toggle 93 | */ 94 | function doDeclareAbstraction($toggle) { 95 | $this->declareAbstraction = (bool)$toggle; 96 | } 97 | 98 | /** 99 | * Generates a markdown formatted table row with information about given function. Then adds the 100 | * row to the table and returns the markdown formatted string. 101 | * 102 | * @param FunctionEntity $func 103 | * @param bool $see 104 | * @return string 105 | */ 106 | function addFunc(FunctionEntity $func, $includeSee=false) 107 | { 108 | $this->fullClassName = $func->getClass(); 109 | 110 | $str = ''; 111 | 112 | if( $this->declareAbstraction && $func->isAbstract() ) 113 | $str .= 'abstract '; 114 | 115 | $str .= $func->getName().'('; 116 | 117 | if( $func->hasParams() ) { 118 | $params = []; 119 | foreach($func->getParams() as $param) { 120 | $paramStr = ''.$param->getType().' '.$param->getName(); 121 | if( $param->getDefault() ) { 122 | $paramStr .= '='.$param->getDefault(); 123 | } 124 | $paramStr .= ''; 125 | $params[] = $paramStr; 126 | } 127 | $str .= ''.implode(', ', $params) .')'; 128 | } else { 129 | $str .= ')'; 130 | } 131 | 132 | $str .= ' : '.$func->getReturnType().''; 133 | 134 | if( $func->isDeprecated() ) { 135 | $str = ''.$str.''; 136 | $str .= '
DEPRECATED - '.$func->getDeprecationMessage().''; 137 | } elseif( $func->getDescription() ) { 138 | $str .= '
'.$func->getDescription().''; 139 | } 140 | if ($func->getSee() && $includeSee) { 141 | $str .= '
    See: ' . 142 | implode(', ', $func->getSee()) . ''; 143 | } 144 | 145 | $str = str_replace(['', ' '], ['',''], trim($str)); 146 | 147 | if( $func->getExample() ) 148 | $this->examples[$func->getName()] = $func->getExample(); 149 | 150 | $firstCol = $func->getVisibility() . ($func->isStatic() ? ' static':''); 151 | $markDown = '| '.$firstCol.' | '.$str.' |'; 152 | 153 | $this->add($markDown); 154 | return $markDown; 155 | } 156 | 157 | /** 158 | * @return string 159 | */ 160 | function getTable() 161 | { 162 | $tbl = trim($this->markdown); 163 | if( $this->appendExamples && !empty($this->examples) ) { 164 | $className = Utils::getClassBaseName($this->fullClassName); 165 | foreach ($this->examples as $funcName => $example) { 166 | $tbl .= sprintf("\n###### Examples of %s::%s()\n%s", $className, $funcName, self::formatExampleComment($example)); 167 | } 168 | } 169 | return $tbl; 170 | } 171 | 172 | /** 173 | * Create a markdown-formatted code view out of an example comment 174 | * @param string $example 175 | * @return string 176 | */ 177 | public static function formatExampleComment($example) 178 | { 179 | // Remove possible code tag 180 | $example = self::stripCodeTags($example); 181 | 182 | if( preg_match('/(\n )/', $example) ) { 183 | $example = preg_replace('/(\n )/', "\n", $example); 184 | } 185 | elseif( preg_match('/(\n )/', $example) ) { 186 | $example = preg_replace('/(\n )/', "\n", $example); 187 | } else { 188 | $example = preg_replace('/(\n )/', "\n", $example); 189 | } 190 | $type = ''; 191 | 192 | // A very naive analysis of the programming language used in the comment 193 | if( strpos($example, 'markdown .= $str .PHP_EOL; 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/PHPDocsMD/ParamEntity.php: -------------------------------------------------------------------------------- 1 | default = $default; 27 | } 28 | 29 | /** 30 | * @return boolean 31 | */ 32 | public function getDefault() 33 | { 34 | return $this->default; 35 | } 36 | 37 | /** 38 | * @param string $type 39 | */ 40 | public function setType($type) 41 | { 42 | $this->type = $type; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getType() 49 | { 50 | return $this->type; 51 | } 52 | 53 | /** 54 | * @return string|null 55 | */ 56 | public function getNativeClassType() 57 | { 58 | foreach(explode('/', $this->type) as $typeDeclaration) { 59 | if (Utils::isNativeClassReference($typeDeclaration)) { 60 | return $typeDeclaration; 61 | } 62 | } 63 | return null; 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/PHPDocsMD/Reflector.php: -------------------------------------------------------------------------------- 1 | className = $className; 61 | $this->functionFinder = $this->loadIfNull($functionFinder, FunctionFinder::class); 62 | $this->docInfoExtractor = $this->loadIfNull($docInfoExtractor, DocInfoExtractor::class); 63 | $this->useInspector = $this->loadIfNull($useInspector, UseInspector::class); 64 | $this->classEntityFactory = $this->loadIfNull( 65 | $classEntityFactory, 66 | ClassEntityFactory::class, 67 | $this->docInfoExtractor 68 | ); 69 | } 70 | 71 | private function loadIfNull($obj, $className, $in=null) 72 | { 73 | return is_object($obj) ? $obj : new $className($in); 74 | } 75 | 76 | /** 77 | * @return \PHPDocsMD\ClassEntity 78 | */ 79 | function getClassEntity() { 80 | $classReflection = new \ReflectionClass($this->className); 81 | $classEntity = $this->classEntityFactory->create($classReflection); 82 | 83 | $classEntity->setFunctions($this->getClassFunctions($classEntity, $classReflection)); 84 | 85 | return $classEntity; 86 | } 87 | 88 | /** 89 | * @param ClassEntity $classEntity 90 | * @param \ReflectionClass $reflectionClass 91 | * @return FunctionEntity[] 92 | */ 93 | private function getClassFunctions(ClassEntity $classEntity, \ReflectionClass $reflectionClass) 94 | { 95 | $classUseStatements = $this->useInspector->getUseStatements($reflectionClass); 96 | $publicFunctions = []; 97 | $protectedFunctions = []; 98 | $methodReflections = []; 99 | 100 | if (count($this->visibilityFilter) === 0) { 101 | $methodReflections = $reflectionClass->getMethods(); 102 | } else { 103 | foreach ($this->visibilityFilter as $filter) { 104 | $methodReflections[] = $reflectionClass->getMethods($this->translateVisibilityFilter($filter)); 105 | } 106 | $methodReflections = call_user_func_array('array_merge', $methodReflections); 107 | } 108 | 109 | if ($this->methodRegex !== '') { 110 | $methodReflections = array_filter($methodReflections, function (ReflectionMethod $reflectionMethod) { 111 | return preg_match($this->methodRegex, $reflectionMethod->name); 112 | }); 113 | } 114 | 115 | foreach($methodReflections as $methodReflection) { 116 | 117 | $func = $this->createFunctionEntity( 118 | $methodReflection, 119 | $classEntity, 120 | $classUseStatements 121 | ); 122 | 123 | 124 | if( $func ) { 125 | if( $func->getVisibility() == 'public' ) { 126 | $publicFunctions[$func->getName()] = $func; 127 | } else { 128 | $protectedFunctions[$func->getName()] = $func; 129 | } 130 | } 131 | } 132 | 133 | ksort($publicFunctions); 134 | ksort($protectedFunctions); 135 | 136 | return array_values(array_merge($publicFunctions, $protectedFunctions)); 137 | } 138 | 139 | /** 140 | * @param ReflectionMethod $method 141 | * @param ClassEntity $class 142 | * @param array $useStatements 143 | * @return bool|FunctionEntity 144 | */ 145 | protected function createFunctionEntity(ReflectionMethod $method, ClassEntity $class, $useStatements) 146 | { 147 | $func = new FunctionEntity(); 148 | $docInfo = $this->docInfoExtractor->extractInfo($method); 149 | $this->docInfoExtractor->applyInfoToEntity($method, $docInfo, $func); 150 | 151 | if ($docInfo->shouldInheritDoc()) { 152 | return $this->findInheritedFunctionDeclaration($func, $class); 153 | } 154 | 155 | if ($this->shouldIgnoreFunction($docInfo, $method, $class)) { 156 | return false; 157 | } 158 | 159 | $returnType = $this->getReturnType($docInfo, $method, $func, $useStatements); 160 | $func->setReturnType($returnType); 161 | $func->setParams($this->getParams($method, $docInfo)); 162 | $func->isStatic($method->isStatic()); 163 | $func->setVisibility($method->isPublic() ? 'public' : 'protected'); 164 | $func->isAbstract($method->isAbstract()); 165 | $func->setClass($class->getName()); 166 | $func->isReturningNativeClass(Utils::isNativeClassReference($returnType)); 167 | 168 | return $func; 169 | } 170 | 171 | /** 172 | * @param DocInfo $docInfo 173 | * @param ReflectionMethod $method 174 | * @param FunctionEntity $func 175 | * @param array $useStatements 176 | * @return string 177 | */ 178 | private function getReturnType( 179 | DocInfo $docInfo, 180 | ReflectionMethod $method, 181 | FunctionEntity $func, 182 | array $useStatements 183 | ) { 184 | $returnType = $docInfo->getReturnType(); 185 | if (empty($returnType)) { 186 | $returnType = $this->guessReturnTypeFromFuncName($func->getName()); 187 | } elseif(Utils::isClassReference($returnType) && !self::classExists($returnType)) { 188 | $isReferenceToArrayOfObjects = substr($returnType, -2) == '[]' ? '[]':''; 189 | if ($isReferenceToArrayOfObjects) { 190 | $returnType = substr($returnType, 0, strlen($returnType)-2); 191 | } 192 | $className = $this->stripAwayNamespace($returnType); 193 | foreach ($useStatements as $usedClass) { 194 | if ($this->stripAwayNamespace($usedClass) == $className) { 195 | $returnType = $usedClass; 196 | break; 197 | } 198 | } 199 | if ($isReferenceToArrayOfObjects) { 200 | $returnType .= '[]'; 201 | } 202 | } 203 | 204 | return Utils::sanitizeDeclaration( 205 | $returnType, 206 | $method->getDeclaringClass()->getNamespaceName() 207 | ); 208 | } 209 | 210 | /** 211 | * @param string $classRef 212 | * @return bool 213 | */ 214 | private function classExists($classRef) 215 | { 216 | return class_exists(trim($classRef, '[]')); 217 | } 218 | 219 | /** 220 | * @param string $className 221 | * @return string 222 | */ 223 | private function stripAwayNamespace($className) 224 | { 225 | return trim(substr($className, strrpos($className, '\\')), '\\'); 226 | } 227 | 228 | /** 229 | * @param DocInfo $info 230 | * @param ReflectionMethod $method 231 | * @param ClassEntity $class 232 | * @return bool 233 | */ 234 | protected function shouldIgnoreFunction($info, ReflectionMethod $method, $class) 235 | { 236 | return $info->shouldBeIgnored() || 237 | $method->isPrivate() || 238 | !$class->isSame($method->getDeclaringClass()->getName()); 239 | } 240 | 241 | /** 242 | * @todo Turn this into a class "FunctionEntityFactory" 243 | * @param \ReflectionParameter $reflection 244 | * @param array $docs 245 | * @return FunctionEntity 246 | */ 247 | private function createParameterEntity(\ReflectionParameter $reflection, $docs) 248 | { 249 | // need to use slash instead of pipe or md-generation will get it wrong 250 | $def = false; 251 | $type = 'mixed'; 252 | $declaredType = self::getParamType($reflection); 253 | if( !isset($docs['type']) ) 254 | $docs['type'] = ''; 255 | 256 | if( $declaredType && !($declaredType=='array' && substr($docs['type'], -2) == '[]') && $declaredType != $docs['type']) { 257 | if( $declaredType && $docs['type'] ) { 258 | $posClassA = Utils::getClassBaseName($docs['type']); 259 | $posClassB = Utils::getClassBaseName($declaredType); 260 | if( $posClassA == $posClassB ) { 261 | $docs['type'] = $declaredType; 262 | } else { 263 | $docs['type'] = empty($docs['type']) ? $declaredType : $docs['type'].'/'.$declaredType; 264 | } 265 | } else { 266 | $docs['type'] = empty($docs['type']) ? $declaredType : $docs['type'].'/'.$declaredType; 267 | } 268 | } 269 | 270 | try { 271 | $def = $reflection->getDefaultValue(); 272 | $type = $this->getTypeFromVal($def); 273 | if( is_string($def) ) { 274 | $def = "`'$def'`"; 275 | } elseif( is_bool($def) ) { 276 | $def = $def ? 'true':'false'; 277 | } elseif( is_null($def) ) { 278 | $def = 'null'; 279 | } elseif( is_array($def) ) { 280 | $def = 'array()'; 281 | } 282 | } catch(\Exception $e) {} 283 | 284 | $varName = '$'.$reflection->getName(); 285 | 286 | if( !empty($docs) ) { 287 | $docs['default'] = $def; 288 | if( $type == 'mixed' && $def == 'null' && strpos($docs['type'], '\\') === 0 ) { 289 | $type = false; 290 | } 291 | if( $type && $def && !empty($docs['type']) && $docs['type'] != $type && strpos($docs['type'], '|') === false) { 292 | if( substr($docs['type'], strpos($docs['type'], '\\')) == substr($declaredType, strpos($declaredType, '\\')) ) { 293 | $docs['type'] = $declaredType; 294 | } else { 295 | $docs['type'] = ($type == 'mixed' ? '':$type.'/').$docs['type']; 296 | } 297 | } elseif( $type && empty($docs['type']) ) { 298 | $docs['type'] = $type; 299 | } 300 | } else { 301 | $docs = [ 302 | 'descriptions'=>'', 303 | 'name' => $varName, 304 | 'default' => $def, 305 | 'type' => $type 306 | ]; 307 | } 308 | 309 | $param = new ParamEntity(); 310 | $param->setDescription(isset($docs['description']) ? $docs['description']:''); 311 | $param->setName($varName); 312 | $param->setDefault($docs['default']); 313 | $param->setType(empty($docs['type']) ? 'mixed':str_replace(['|', '\\\\'], ['/', '\\'], $docs['type'])); 314 | return $param; 315 | } 316 | 317 | /** 318 | * Tries to find out if the type of the given parameter. Will 319 | * return empty string if not possible. 320 | * 321 | * @example 322 | * 323 | * getMethods() as $method ) { 326 | * foreach($method->getParameters() as $param) { 327 | * $name = $param->getName(); 328 | * $type = Reflector::getParamType($param); 329 | * printf("%s = %s\n", $name, $type); 330 | * } 331 | * } 332 | * 333 | * 334 | * @param \ReflectionParameter $refParam 335 | * @return string 336 | */ 337 | static function getParamType(\ReflectionParameter $refParam) 338 | { 339 | $export = str_replace(' or NULL', '', (string)$refParam); 340 | 341 | $type = preg_replace('/.*?([\w\\\]+)\s+\$'.current(explode('=', $refParam->name)).'.*/', '\\1', $export); 342 | if( strpos($type, 'Parameter ') !== false ) { 343 | return ''; 344 | } 345 | 346 | if( $type != 'array' && strpos($type, '\\') !== 0 ) { 347 | $type = '\\'.$type; 348 | } 349 | 350 | return $type; 351 | } 352 | 353 | /** 354 | * @param string $name 355 | * @return string 356 | */ 357 | private function guessReturnTypeFromFuncName($name) 358 | { 359 | $mixed = ['get', 'load', 'fetch', 'find', 'create']; 360 | $bool = ['is', 'can', 'has', 'have', 'should']; 361 | foreach($mixed as $prefix) { 362 | if( strpos($name, $prefix) === 0 ) 363 | return 'mixed'; 364 | } 365 | foreach($bool as $prefix) { 366 | if( strpos($name, $prefix) === 0 ) 367 | return 'bool'; 368 | } 369 | return 'void'; 370 | } 371 | 372 | /** 373 | * @param string $def 374 | * @return string 375 | */ 376 | private function getTypeFromVal($def) 377 | { 378 | if( is_string($def) ) { 379 | return 'string'; 380 | } elseif( is_bool($def) ) { 381 | return 'bool'; 382 | } elseif( is_array($def) ) { 383 | return 'array'; 384 | } else { 385 | return 'mixed'; 386 | } 387 | } 388 | 389 | /** 390 | * @param FunctionEntity $func 391 | * @param ClassEntity $class 392 | * @return FunctionEntity 393 | */ 394 | private function findInheritedFunctionDeclaration(FunctionEntity $func, ClassEntity $class) 395 | { 396 | $funcName = $func->getName(); 397 | $inheritedFuncDeclaration = $this->functionFinder->find( 398 | $funcName, 399 | $class->getExtends() 400 | ); 401 | if (!$inheritedFuncDeclaration) { 402 | $inheritedFuncDeclaration = $this->functionFinder->findInClasses( 403 | $funcName, 404 | $class->getInterfaces() 405 | ); 406 | if (!$inheritedFuncDeclaration) { 407 | throw new \RuntimeException( 408 | 'Function '.$funcName.' tries to inherit docs but no parent method is found' 409 | ); 410 | } 411 | } 412 | if (!$func->isAbstract() && !$class->isAbstract() && $inheritedFuncDeclaration->isAbstract()) { 413 | $inheritedFuncDeclaration->isAbstract(false); 414 | } 415 | return $inheritedFuncDeclaration; 416 | } 417 | 418 | /** 419 | * @param ReflectionMethod $method 420 | * @param DocInfo $docInfo 421 | * @return array 422 | */ 423 | private function getParams(ReflectionMethod $method, $docInfo) 424 | { 425 | $params = []; 426 | foreach ($method->getParameters() as $param) { 427 | $paramName = '$' . $param->getName(); 428 | $params[$param->getName()] = $this->createParameterEntity( 429 | $param, 430 | $docInfo->getParameterInfo($paramName) 431 | ); 432 | } 433 | return array_values($params); 434 | } 435 | 436 | private function translateVisibilityFilter($filter){ 437 | $map = [ 438 | 'public' => ReflectionMethod::IS_PUBLIC, 439 | 'protected' => ReflectionMethod::IS_PROTECTED, 440 | 'abstract' => ReflectionMethod::IS_ABSTRACT, 441 | 'final' => ReflectionMethod::IS_FINAL, 442 | ]; 443 | 444 | return isset($map[$filter]) ? $map[$filter] : null; 445 | } 446 | 447 | public function setVisibilityFilter( array $visibilityFilter ) 448 | { 449 | $this->visibilityFilter = $visibilityFilter; 450 | } 451 | 452 | public function setMethodRegex($methodRegex) 453 | { 454 | $this->methodRegex = $methodRegex; 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /src/PHPDocsMD/ReflectorInterface.php: -------------------------------------------------------------------------------- 1 | getUseStatementsInString(file_get_contents($filePath)); 34 | } 35 | 36 | /** 37 | * @param \ReflectionClass $reflectionClass 38 | * @return array 39 | */ 40 | public function getUseStatements(\ReflectionClass $reflectionClass) 41 | { 42 | $classUseStatements = []; 43 | $classFile = $reflectionClass->getFileName(); 44 | if ($classFile) { 45 | $classUseStatements = $this->getUseStatementsInFile($classFile); 46 | } 47 | return $classUseStatements; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/PHPDocsMD/Utils.php: -------------------------------------------------------------------------------- 1 | $p) { 39 | if (self::shouldPrefixWithNamespace($p)) { 40 | $p = self::sanitizeClassName('\\' . trim($currentNameSpace, '\\') . '\\' . $p); 41 | } elseif (self::isClassReference($p)) { 42 | $p = self::sanitizeClassName($p); 43 | } 44 | $parts[$i] = $p; 45 | } 46 | return implode('/', $parts); 47 | } 48 | 49 | /** 50 | * @param string $typeDeclaration 51 | * @return bool 52 | */ 53 | private static function shouldPrefixWithNameSpace($typeDeclaration) 54 | { 55 | return strpos($typeDeclaration, '\\') !== 0 && self::isClassReference($typeDeclaration); 56 | } 57 | 58 | /** 59 | * @param string $typeDeclaration 60 | * @return bool 61 | */ 62 | public static function isClassReference($typeDeclaration) 63 | { 64 | $natives = [ 65 | 'mixed', 66 | 'string', 67 | 'int', 68 | 'float', 69 | 'integer', 70 | 'number', 71 | 'bool', 72 | 'boolean', 73 | 'object', 74 | 'false', 75 | 'true', 76 | 'null', 77 | 'array', 78 | 'void', 79 | 'callable' 80 | ]; 81 | $sanitizedTypeDeclaration = rtrim(trim(strtolower($typeDeclaration)), '[]'); 82 | 83 | return !in_array($sanitizedTypeDeclaration, $natives) && 84 | strpos($typeDeclaration, ' ') === false; 85 | } 86 | 87 | public static function isNativeClassReference($typeDeclaration) 88 | { 89 | $sanitizedType = str_replace('[]', '', $typeDeclaration); 90 | if (Utils::isClassReference($typeDeclaration) && class_exists($sanitizedType, false)) { 91 | $reflectionClass = new \ReflectionClass($sanitizedType); 92 | return !$reflectionClass->getFileName(); 93 | } 94 | return false; 95 | } 96 | } -------------------------------------------------------------------------------- /test/ExampleClass.php: -------------------------------------------------------------------------------- 1 | 40 | * 44 | * 45 | * @param int $arg 46 | * @param array $arr 47 | * @param int $bool 48 | */ 49 | function funcB($arg, array $arr, $bool=10) { 50 | 51 | } 52 | 53 | function funcD($arg, $arr=array(), ExampleInterface $depr=null, \stdClass $class = null) { 54 | 55 | } 56 | 57 | function getFunc() {} 58 | function hasFunc() {} 59 | abstract function isFunc(); 60 | 61 | /** 62 | * @ignore 63 | */ 64 | function someFunc() { 65 | 66 | } 67 | 68 | private function privFunc() { 69 | 70 | } 71 | 72 | } 73 | 74 | /** 75 | * @deprecated This one is deprecated 76 | * 77 | * Lorem te ipsum 78 | * 79 | * @package Acme 80 | */ 81 | class ExampleClassDepr { 82 | 83 | } 84 | 85 | /** 86 | * Interface ExampleInterface 87 | * @package Acme 88 | * @ignore 89 | */ 90 | interface ExampleInterface { 91 | 92 | /** 93 | * @param string $arg 94 | * @return \stdClass 95 | */ 96 | public function func($arg='a'); 97 | 98 | } 99 | 100 | class SomeClass { 101 | 102 | /** 103 | * @return int 104 | */ 105 | public function aMethod() {} 106 | 107 | } 108 | 109 | class ClassImplementingInterface extends SomeClass implements ExampleInterface { 110 | /** 111 | * @inheritdoc 112 | */ 113 | public function func($arg='a') {} 114 | 115 | /** 116 | * @inheritDoc 117 | */ 118 | public function aMethod() {} 119 | 120 | /** 121 | * @return \FilesystemIterator 122 | */ 123 | public function methodReturnNativeClass() {} 124 | 125 | /** 126 | * @return \FilesystemIterator[] 127 | */ 128 | public function methodReturningArrayNativeClass() {} 129 | } 130 | 131 | 132 | 133 | class ClassWithStaticFunc { 134 | 135 | /** 136 | * @return float 137 | */ 138 | static function somStaticFunc() { 139 | 140 | } 141 | 142 | } 143 | 144 | use PHPDocsMD\Console\CLI; 145 | 146 | interface InterfaceReferringToImportedClass { 147 | 148 | /** 149 | * @return CLI 150 | */ 151 | function theFunc(); 152 | 153 | /** 154 | * @return CLI[] 155 | */ 156 | function funcReturningArr(); 157 | 158 | } 159 | -------------------------------------------------------------------------------- /test/MDTableGeneratorTest.php: -------------------------------------------------------------------------------- 1 | openTable(); 12 | 13 | $deprecated = new \PHPDocsMD\FunctionEntity(); 14 | $deprecated->isDeprecated(true); 15 | $deprecated->setDeprecationMessage('Is deprecated'); 16 | $deprecated->setName('myFunc'); 17 | $deprecated->setReturnType('mixed'); 18 | 19 | $this->assertTrue($deprecated->isDeprecated()); 20 | 21 | $tbl->addFunc($deprecated); 22 | 23 | $tblMarkdown = $tbl->getTable(); 24 | $expect = '| Visibility | Function |'.PHP_EOL. 25 | '|:-----------|:---------|'.PHP_EOL. 26 | '| public | myFunc() : mixed
DEPRECATED - Is deprecated |'; 27 | 28 | $this->assertEquals($expect, $tblMarkdown); 29 | } 30 | 31 | function testFunc() 32 | { 33 | $tbl = new \PHPDocsMD\MDTableGenerator(); 34 | $tbl->openTable(); 35 | 36 | $func = new \PHPDocsMD\FunctionEntity(); 37 | $func->setName('myFunc'); 38 | $tbl->addFunc($func); 39 | 40 | $tblMarkdown = $tbl->getTable(); 41 | $expect = '| Visibility | Function |'.PHP_EOL. 42 | '|:-----------|:---------|'.PHP_EOL. 43 | '| public | myFunc() : void |'; 44 | 45 | $this->assertEquals($expect, $tblMarkdown); 46 | } 47 | 48 | function testFuncWithAllFeatures() 49 | { 50 | $tbl = new \PHPDocsMD\MDTableGenerator(); 51 | $tbl->openTable(); 52 | 53 | $func = new \PHPDocsMD\FunctionEntity(); 54 | 55 | $this->assertFalse($func->isStatic()); 56 | $this->assertFalse($func->hasParams()); 57 | $this->assertFalse($func->isDeprecated()); 58 | $this->assertFalse($func->isAbstract()); 59 | $this->assertEquals('public', $func->getVisibility()); 60 | 61 | $func->isStatic(true); 62 | $func->setVisibility('protected'); 63 | $func->setName('someFunc'); 64 | $func->setDescription('desc...'); 65 | $func->setReturnType('\\stdClass'); 66 | 67 | $params = array(); 68 | 69 | $paramA = new \PHPDocsMD\ParamEntity(); 70 | $paramA->setName('$var'); 71 | $paramA->setType('mixed'); 72 | $paramA->setDefault('null'); 73 | $params[] = $paramA; 74 | 75 | $paramB = new \PHPDocsMD\ParamEntity(); 76 | $paramB->setName('$other'); 77 | $paramB->setType('string'); 78 | $paramB->setDefault("'test'"); 79 | $params[] = $paramB; 80 | 81 | $func->setParams($params); 82 | 83 | $tbl->addFunc($func); 84 | 85 | $this->assertTrue($func->isStatic()); 86 | $this->assertTrue($func->hasParams()); 87 | $this->assertEquals('protected', $func->getVisibility()); 88 | 89 | $tblMarkdown = $tbl->getTable(); 90 | $expect = '| Visibility | Function |'.PHP_EOL. 91 | '|:-----------|:---------|'.PHP_EOL. 92 | '| protected static | someFunc(mixed $var=null, string $other=\'test\') : \\stdClass
desc... |'; 93 | 94 | $this->assertEquals($expect, $tblMarkdown); 95 | } 96 | 97 | function testToggleDeclaringAbstraction() 98 | { 99 | $tbl = new \PHPDocsMD\MDTableGenerator(); 100 | $tbl->openTable(); 101 | 102 | $func = new \PHPDocsMD\FunctionEntity(); 103 | $func->isAbstract(true); 104 | $func->setName('someFunc'); 105 | 106 | $tbl->addFunc($func); 107 | $tblMarkdown = $tbl->getTable(); 108 | $expect = '| Visibility | Function |'.PHP_EOL. 109 | '|:-----------|:---------|'.PHP_EOL. 110 | '| public | abstract someFunc() : void |'; 111 | 112 | $this->assertEquals($expect, $tblMarkdown); 113 | 114 | $tbl->openTable(); 115 | $tbl->doDeclareAbstraction(false); 116 | $tbl->addFunc($func); 117 | 118 | $tblMarkdown = $tbl->getTable(); 119 | $expect = '| Visibility | Function |'.PHP_EOL. 120 | '|:-----------|:---------|'.PHP_EOL. 121 | '| public | someFunc() : void |'; 122 | 123 | $this->assertEquals($expect, $tblMarkdown); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /test/ReflectorTest.php: -------------------------------------------------------------------------------- 1 | reflector = new Reflector('Acme\\ExampleClass'); 24 | $this->class = $this->reflector->getClassEntity(); 25 | } 26 | 27 | function testClass() 28 | { 29 | $this->assertEquals('\\Acme\\ExampleClass', $this->class->getName()); 30 | $this->assertEquals('This is a description of this class', $this->class->getDescription()); 31 | $this->assertEquals('Class: \\Acme\\ExampleClass (abstract)', $this->class->generateTitle()); 32 | $this->assertEquals('class-acmeexampleclass-abstract', $this->class->generateAnchor()); 33 | $this->assertFalse($this->class->isDeprecated()); 34 | $this->assertFalse($this->class->hasIgnoreTag()); 35 | 36 | $refl = new Reflector('Acme\\ExampleClassDepr'); 37 | $class = $refl->getClassEntity(); 38 | $this->assertTrue($class->isDeprecated()); 39 | $this->assertEquals('This one is deprecated Lorem te ipsum', $class->getDeprecationMessage()); 40 | $this->assertFalse($class->hasIgnoreTag()); 41 | 42 | $refl = new Reflector('Acme\\ExampleInterface'); 43 | $class = $refl->getClassEntity(); 44 | $this->assertTrue($class->isInterface()); 45 | $this->assertTrue($class->hasIgnoreTag()); 46 | } 47 | 48 | function testFunctions() 49 | { 50 | 51 | $functions = $this->class->getFunctions(); 52 | 53 | $this->assertNotEmpty($functions); 54 | 55 | $this->assertEquals('Description of a*a', $functions[0]->getDescription()); 56 | $this->assertEquals(false, $functions[0]->isDeprecated()); 57 | $this->assertEquals('funcA', $functions[0]->getName()); 58 | $this->assertEquals('void', $functions[0]->getReturnType()); 59 | $this->assertEquals('public', $functions[0]->getVisibility()); 60 | 61 | $this->assertEquals('Description of b', $functions[1]->getDescription()); 62 | $this->assertEquals(false, $functions[1]->isDeprecated()); 63 | $this->assertEquals('funcB', $functions[1]->getName()); 64 | $this->assertEquals('void', $functions[1]->getReturnType()); 65 | $this->assertEquals('public', $functions[1]->getVisibility()); 66 | 67 | 68 | $this->assertEquals('', $functions[2]->getDescription()); 69 | $this->assertEquals('funcD', $functions[2]->getName()); 70 | $this->assertEquals('void', $functions[2]->getReturnType()); 71 | $this->assertEquals('public', $functions[2]->getVisibility()); 72 | $this->assertEquals(false, $functions[2]->isDeprecated()); 73 | 74 | // These function does not declare return type but the return 75 | // type should be guessable 76 | $this->assertEquals('mixed', $functions[3]->getReturnType()); 77 | $this->assertEquals('bool', $functions[4]->getReturnType()); 78 | $this->assertEquals('bool', $functions[5]->getReturnType()); 79 | $this->assertTrue($functions[5]->isAbstract()); 80 | $this->assertTrue($this->class->isAbstract()); 81 | 82 | // Protected function have been put last 83 | $this->assertEquals('Description of c', $functions[6]->getDescription()); 84 | $this->assertEquals(true, $functions[6]->isDeprecated()); 85 | $this->assertEquals('This one is deprecated', $functions[6]->getDeprecationMessage()); 86 | $this->assertEquals('funcC', $functions[6]->getName()); 87 | $this->assertEquals('\\Acme\\ExampleClass', $functions[6]->getReturnType()); 88 | $this->assertEquals('protected', $functions[6]->getVisibility()); 89 | 90 | $this->assertTrue( empty($functions[7]) ); // Should be skipped since tagged with @ignore */ 91 | } 92 | 93 | function testStaticFunc() { 94 | $reflector = new Reflector('Acme\\ClassWithStaticFunc'); 95 | $functions = $reflector->getClassEntity()->getFunctions(); 96 | $this->assertNotEmpty($functions); 97 | $this->assertEquals('', $functions[0]->getDescription()); 98 | $this->assertEquals(false, $functions[0]->isDeprecated()); 99 | $this->assertEquals(true, $functions[0]->isStatic()); 100 | $this->assertEquals('', $functions[0]->getDeprecationMessage()); 101 | $this->assertEquals('somStaticFunc', $functions[0]->getName()); 102 | $this->assertEquals('public', $functions[0]->getVisibility()); 103 | $this->assertEquals('float', $functions[0]->getReturnType()); 104 | } 105 | 106 | function testParams() 107 | { 108 | $paramA = new ReflectionParameter(array('Acme\\ExampleClass', 'funcD'), 2); 109 | $paramB = new ReflectionParameter(array('Acme\\ExampleClass', 'funcD'), 3); 110 | $paramC = new ReflectionParameter(array('Acme\\ExampleClass', 'funcD'), 0); 111 | 112 | $typeA = Reflector::getParamType($paramA); 113 | $typeB = Reflector::getParamType($paramB); 114 | $typeC = Reflector::getParamType($paramC); 115 | 116 | $this->assertEmpty($typeC); 117 | $this->assertEquals('\\stdClass', $typeB); 118 | $this->assertEquals('\\Acme\\ExampleInterface', $typeA); 119 | 120 | $functions = $this->class->getFunctions(); 121 | 122 | $this->assertTrue($functions[2]->hasParams()); 123 | $this->assertFalse($functions[5]->hasParams()); 124 | 125 | $params = $functions[1]->getParams(); 126 | $this->assertEquals('int', $params[0]->getType()); 127 | 128 | $params = $functions[2]->getParams(); 129 | $this->assertEquals(4, count($params)); 130 | $this->assertEquals(false, $params[0]->getDefault()); 131 | $this->assertEquals('$arg', $params[0]->getName()); 132 | $this->assertEquals('mixed', $params[0]->getType()); 133 | $this->assertEquals('array()', $params[1]->getDefault()); 134 | $this->assertEquals('$arr', $params[1]->getName()); 135 | $this->assertEquals('array', $params[1]->getType()); 136 | $this->assertEquals('null', $params[2]->getDefault()); 137 | $this->assertEquals('$depr', $params[2]->getName()); 138 | $this->assertEquals('\\Acme\\ExampleInterface', $params[2]->getType()); 139 | } 140 | 141 | function testInheritedDocs() 142 | { 143 | $reflector = new Reflector('Acme\\ClassImplementingInterface'); 144 | $functions = $reflector->getClassEntity()->getFunctions(); 145 | $this->assertEquals(4, count($functions)); 146 | $this->assertEquals('aMethod', $functions[0]->getName()); 147 | $this->assertEquals('int', $functions[0]->getReturnType()); 148 | $this->assertFalse($functions[0]->isReturningNativeClass()); 149 | $this->assertEquals('func', $functions[1]->getName()); 150 | $this->assertEquals('\\stdClass', $functions[1]->getReturnType()); 151 | $this->assertFalse($functions[1]->isAbstract()); 152 | 153 | $this->assertTrue($functions[2]->isReturningNativeClass()); 154 | $this->assertTrue($functions[3]->isReturningNativeClass()); 155 | } 156 | 157 | 158 | function testReferenceToImportedClass() 159 | { 160 | $reflector = new Reflector('Acme\\InterfaceReferringToImportedClass'); 161 | $functions = $reflector->getClassEntity()->getFunctions(); 162 | $this->assertEquals('\\PHPDocsMD\\Console\\CLI', $functions[1]->getReturnType()); 163 | $this->assertEquals('\\PHPDocsMD\\Console\\CLI[]', $functions[0]->getReturnType()); 164 | } 165 | 166 | public function visibilityFiltersAndExpectedMethods() 167 | { 168 | return [ 169 | 'public' => [['public'], ['funcA', 'funcB', 'funcD', 'getFunc', 'hasFunc', 'isFunc']], 170 | 'protected' => [['protected'], ['funcC']], 171 | 'public-and-protected' => [ 172 | ['public', 'protected'], 173 | ['funcA', 'funcB', 'funcD', 'getFunc', 'hasFunc', 'isFunc', 'funcC'], 174 | ], 175 | 'abstract' => [['abstract'], ['isFunc']], 176 | ]; 177 | } 178 | 179 | /** 180 | *@dataProvider visibilityFiltersAndExpectedMethods 181 | */ 182 | public function testVisibilityBasedFiltering(array $visibilityFilter, array $expectedMethods) 183 | { 184 | $reflector = new Reflector('Acme\\ExampleClass'); 185 | $reflector->setVisibilityFilter($visibilityFilter); 186 | $functions = $reflector->getClassEntity()->getFunctions(); 187 | $functionNames = array_map( 188 | function (FunctionEntity $entity) { 189 | return $entity->getName(); 190 | }, 191 | $functions 192 | ); 193 | $this->assertEquals($expectedMethods, $functionNames); 194 | } 195 | 196 | public function regexFiltersAndExpectedMethods() 197 | { 198 | return [ 199 | 'has-only' => ['/^has/', ['hasFunc']], 200 | 'does-not-start-with-h' => ['/^[^h]/', ['funcA', 'funcB', 'funcD', 'getFunc', 'isFunc', 'funcC']], 201 | 'func-letter-only' => ['/^func[A-Z]/', ['funcA', 'funcB', 'funcD', 'funcC']], 202 | ]; 203 | } 204 | 205 | /** 206 | *@dataProvider regexFiltersAndExpectedMethods 207 | */ 208 | public function testMethodRegexFiltering($regexFilter, $expectedMethods) 209 | { 210 | $reflector = new Reflector('Acme\\ExampleClass'); 211 | $reflector->setMethodRegex($regexFilter); 212 | $functions = $reflector->getClassEntity()->getFunctions(); 213 | $functionNames = array_map( 214 | function (FunctionEntity $entity) { 215 | return $entity->getName(); 216 | }, 217 | $functions 218 | ); 219 | $this->assertEquals($expectedMethods, $functionNames); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /test/UseInspectorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $inspector->getUseStatementsInString($code)); 42 | } 43 | } 44 | --------------------------------------------------------------------------------