mirror of
https://github.com/danog/phpdoc.git
synced 2024-11-26 12:04:47 +01:00
Finalize
This commit is contained in:
parent
864295ff3f
commit
78cb0ae5da
@ -5,13 +5,18 @@ use danog\PhpDoc\PhpDocBuilder;
|
||||
|
||||
if ($argc < 2) {
|
||||
$me = $argv[0];
|
||||
fprintf(stderr, "Usage: $me filePath [namespace]\n");
|
||||
fprintf(STDERR, "Usage: $me filePath [namespace]".PHP_EOL);
|
||||
die(1);
|
||||
}
|
||||
|
||||
if (!class_exists(PhpDocBuilder::class)) {
|
||||
require 'vendor/autoload.php';
|
||||
}
|
||||
|
||||
$path = $argv[1];
|
||||
$namespace = $argv[2] ?? '';
|
||||
|
||||
|
||||
PhpDocBuilder::fromNamespace($namespace)
|
||||
->setOutput($path)
|
||||
->run();
|
||||
|
122
docs/danog/PhpDoc/PhpDocBuilder.md
Normal file
122
docs/danog/PhpDoc/PhpDocBuilder.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: danog\PhpDoc\PhpDocBuilder: PHP documentation builder.
|
||||
description:
|
||||
|
||||
---
|
||||
# `danog\PhpDoc\PhpDocBuilder`
|
||||
[Back to index](../../index.md)
|
||||
|
||||
> Author: Daniil Gentili <daniil@daniil.it>
|
||||
|
||||
|
||||
PHP documentation builder.
|
||||
|
||||
|
||||
|
||||
## Method list:
|
||||
* `fromNamespace(string $namespace): self`
|
||||
* `setAuthors(\phpDocumentor\Reflection\DocBlock\Tags\Author[] $authors): self`
|
||||
* `setMode(int $mode): self`
|
||||
* `setFilter(callable $ignore): self`
|
||||
* `setOutput(string $output): self`
|
||||
* `setName(string $name): self`
|
||||
* `setDescription(string $description): self`
|
||||
* `setImage(string $image): self`
|
||||
* `run(): self`
|
||||
|
||||
## Methods:
|
||||
### `fromNamespace(string $namespace): self`
|
||||
|
||||
Create docblock builder.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$namespace`: `string` Namespace (defaults to package namespace)
|
||||
|
||||
|
||||
|
||||
### `setAuthors(\phpDocumentor\Reflection\DocBlock\Tags\Author[] $authors): self`
|
||||
|
||||
Set authors.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$authors`: `\phpDocumentor\Reflection\DocBlock\Tags\Author[]` Authors
|
||||
|
||||
|
||||
#### See also:
|
||||
* `\phpDocumentor\Reflection\DocBlock\Tags\Author`
|
||||
|
||||
|
||||
|
||||
|
||||
### `setMode(int $mode): self`
|
||||
|
||||
Set scan mode.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$mode`: `int` Scan mode.
|
||||
|
||||
|
||||
|
||||
### `setFilter(callable $ignore): self`
|
||||
|
||||
Set filter to ignore certain classes.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$ignore`: `callable`
|
||||
Full type:
|
||||
```
|
||||
callable(class-string)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### `setOutput(string $output): self`
|
||||
|
||||
Set output directory.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$output`: `string` Output directory
|
||||
|
||||
|
||||
|
||||
### `setName(string $name): self`
|
||||
|
||||
Set project name.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$name`: `string` Name
|
||||
|
||||
|
||||
|
||||
### `setDescription(string $description): self`
|
||||
|
||||
Set project description.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$description`: `string` Project description
|
||||
|
||||
|
||||
|
||||
### `setImage(string $image): self`
|
||||
|
||||
Set project image.
|
||||
|
||||
|
||||
Parameters:
|
||||
* `$image`: `string` Project image
|
||||
|
||||
|
||||
|
||||
### `run(): self`
|
||||
|
||||
Run documentation builder.
|
||||
|
||||
|
||||
|
15
docs/index.md
Normal file
15
docs/index.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: danog/phpdoc
|
||||
description: Simple markdown PHPDOC documentation generator with psalm type annotation support.
|
||||
---
|
||||
# `danog/phpdoc`
|
||||
|
||||
Simple markdown PHPDOC documentation generator with psalm type annotation support.
|
||||
|
||||
|
||||
|
||||
|
||||
## Classes
|
||||
* [\danog\PhpDoc\PhpDocBuilder: PHP documentation builder.](danog/PhpDoc/PhpDocBuilder.md)
|
||||
|
||||
|
15
psalm.xml
Normal file
15
psalm.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="5"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
@ -9,6 +9,11 @@ use ReflectionClass;
|
||||
use ReflectionClassConstant;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Class documentation builder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ClassDoc extends GenericDoc
|
||||
{
|
||||
/**
|
||||
@ -33,7 +38,7 @@ class ClassDoc extends GenericDoc
|
||||
$this->name = $reflectionClass->getName();
|
||||
$doc = $reflectionClass->getDocComment();
|
||||
if (!$doc) {
|
||||
\fprintf(STDERR, $reflectionClass->getName()." has no PHPDOC\n");
|
||||
\fprintf(STDERR, $reflectionClass->getName()." has no PHPDOC".PHP_EOL);
|
||||
$this->ignore = true;
|
||||
return;
|
||||
}
|
||||
@ -43,7 +48,8 @@ class ClassDoc extends GenericDoc
|
||||
|
||||
$tags = $doc->getTags();
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag instanceof Property) {
|
||||
if ($tag instanceof Property && $tag->getVariableName()) {
|
||||
/** @psalm-suppress InvalidPropertyAssignmentValue */
|
||||
$this->properties[$tag->getVariableName()] = [
|
||||
$tag->getType(),
|
||||
$tag->getDescription()
|
||||
@ -55,6 +61,7 @@ class ClassDoc extends GenericDoc
|
||||
[$varName, $description] = \explode(" ", $description, 2);
|
||||
$type = \str_replace('@property ', '', $type);
|
||||
$description ??= '';
|
||||
/** @psalm-suppress InvalidPropertyAssignmentValue */
|
||||
$this->properties[$varName] = [
|
||||
$type,
|
||||
$description
|
||||
@ -101,6 +108,11 @@ class ClassDoc extends GenericDoc
|
||||
$this->methods = \array_filter($this->methods, fn (MethodDoc $doc): bool => !$doc->shouldIgnore());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate markdown for class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(): string
|
||||
{
|
||||
$init = parent::format();
|
||||
|
@ -4,15 +4,26 @@ namespace danog\PhpDoc\PhpDoc;
|
||||
|
||||
use ReflectionFunction;
|
||||
|
||||
/**
|
||||
* Function documentation builder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FunctionDoc extends MethodDoc
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param PhpDoc $builder
|
||||
* @param ReflectionFunction $reflectionClass
|
||||
*/
|
||||
public function __construct(PhpDoc $builder, ReflectionFunction $reflectionClass)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
$this->nameGenericDoc = $reflectionClass->getName();
|
||||
$this->name = $reflectionClass->getName();
|
||||
$doc = $reflectionClass->getDocComment();
|
||||
if (!$doc) {
|
||||
\fprintf(STDERR, $reflectionClass->getName()." has no PHPDOC\n");
|
||||
\fprintf(STDERR, $reflectionClass->getName()." has no PHPDOC".PHP_EOL);
|
||||
$this->ignore = true;
|
||||
return;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace danog\PhpDoc\PhpDoc;
|
||||
|
||||
use danog\PhpDoc\PhpDocBuilder;
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
use phpDocumentor\Reflection\DocBlock\Description;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Author;
|
||||
@ -12,7 +11,14 @@ use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\See;
|
||||
use phpDocumentor\Reflection\Fqsen as ReflectionFqsen;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
|
||||
/**
|
||||
* Generic documentation builder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class GenericDoc
|
||||
{
|
||||
/**
|
||||
@ -59,6 +65,12 @@ abstract class GenericDoc
|
||||
* Fully qualified class name.
|
||||
*/
|
||||
private string $resolvedClassName;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param DocBlock $doc
|
||||
* @param ReflectionClass|ReflectionFunction $reflectionClass
|
||||
*/
|
||||
public function __construct(DocBlock $doc, $reflectionClass)
|
||||
{
|
||||
$empty = [];
|
||||
@ -89,6 +101,11 @@ abstract class GenericDoc
|
||||
$this->authors = \array_unique($this->authors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get see also list.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function seeAlso(): string
|
||||
{
|
||||
$empty = [];
|
||||
@ -145,6 +162,11 @@ abstract class GenericDoc
|
||||
}
|
||||
return $seeAlso;
|
||||
}
|
||||
/**
|
||||
* Generate markdown.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(): string
|
||||
{
|
||||
$authors = '';
|
||||
@ -154,8 +176,8 @@ abstract class GenericDoc
|
||||
$seeAlso = $this->seeAlso();
|
||||
$image = $this->builder->getImage();
|
||||
$index = '';
|
||||
$count = count(explode('\\', $this->resolvedClassName)) - 1;
|
||||
$index .= str_repeat($count, '../');
|
||||
$count = \count(\explode('\\', $this->resolvedClassName)) - 2;
|
||||
$index .= \str_repeat('../', $count);
|
||||
$index .= 'index.md';
|
||||
return <<<EOF
|
||||
---
|
||||
@ -174,10 +196,16 @@ abstract class GenericDoc
|
||||
$seeAlso
|
||||
EOF;
|
||||
}
|
||||
public function resolveTypeAlias(string $o): string
|
||||
/**
|
||||
* Resolve type alias.
|
||||
*
|
||||
* @param string $type Type
|
||||
* @return string
|
||||
*/
|
||||
public function resolveTypeAlias(string $type): string
|
||||
{
|
||||
$resolved = [];
|
||||
$result = $this->builder->resolveTypeAlias($this->className, $o, $resolved);
|
||||
$result = $this->builder->resolveTypeAlias($this->className, $type, $resolved);
|
||||
foreach ($resolved as $type) {
|
||||
if (PhpDoc::isScalar($type)) {
|
||||
continue;
|
||||
@ -196,6 +224,11 @@ abstract class GenericDoc
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we should not store this class.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldIgnore(): bool
|
||||
{
|
||||
return $this->ignore;
|
||||
|
@ -9,12 +9,23 @@ use phpDocumentor\Reflection\DocBlock\Tags\Return_;
|
||||
use ReflectionFunctionAbstract;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Method documentation builder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MethodDoc extends GenericDoc
|
||||
{
|
||||
private Return_ $return;
|
||||
private string $psalmReturn;
|
||||
private array $params = [];
|
||||
private array $psalmParams = [];
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param PhpDoc $phpDocBuilder
|
||||
* @param ReflectionFunctionAbstract $method
|
||||
*/
|
||||
public function __construct(PhpDoc $phpDocBuilder, ReflectionFunctionAbstract $method)
|
||||
{
|
||||
$this->builder = $phpDocBuilder;
|
||||
@ -23,9 +34,9 @@ class MethodDoc extends GenericDoc
|
||||
if (!$doc) {
|
||||
$this->ignore = true;
|
||||
if ($method instanceof ReflectionMethod) {
|
||||
\fprintf(STDERR, $method->getDeclaringClass()->getName().'::'.$method->getName().' has no PHPDOC!\n');
|
||||
\fprintf(STDERR, $method->getDeclaringClass()->getName().'::'.$method->getName().' has no PHPDOC!'.PHP_EOL);
|
||||
} else {
|
||||
\fprintf(STDERR, $method->getName().' has no PHPDOC!\n');
|
||||
\fprintf(STDERR, $method->getName()." has no PHPDOC!".PHP_EOL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -74,6 +85,11 @@ class MethodDoc extends GenericDoc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method signature.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSignature(): string
|
||||
{
|
||||
$sig = $this->name;
|
||||
@ -91,6 +107,11 @@ class MethodDoc extends GenericDoc
|
||||
}
|
||||
return $sig;
|
||||
}
|
||||
/**
|
||||
* Generate markdown for method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(): string
|
||||
{
|
||||
$sig = '### `'.$this->getSignature()."`";
|
||||
|
@ -24,6 +24,11 @@ use phpDocumentor\Reflection\DocBlockFactory;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
|
||||
/**
|
||||
* Documentation builder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PhpDoc
|
||||
{
|
||||
/**
|
||||
@ -33,7 +38,7 @@ class PhpDoc
|
||||
/**
|
||||
* Scan mode.
|
||||
*/
|
||||
private int $mode;
|
||||
private int $mode = ClassFinder::ALLOW_ALL | ClassFinder::RECURSIVE_MODE;
|
||||
/**
|
||||
* Docblock factory.
|
||||
*/
|
||||
@ -64,7 +69,7 @@ class PhpDoc
|
||||
/**
|
||||
* Project image.
|
||||
*/
|
||||
private string $image;
|
||||
private string $image = '';
|
||||
/**
|
||||
* Use map.
|
||||
*
|
||||
@ -162,7 +167,7 @@ class PhpDoc
|
||||
*/
|
||||
public function run(): self
|
||||
{
|
||||
$classList = ClassFinder::getClassesInNamespace($this->namespace, $this->mode | ClassFinder::RECURSIVE_MODE);
|
||||
$classList = ClassFinder::getClassesInNamespace($this->namespace, $this->mode);
|
||||
foreach ($classList as $class) {
|
||||
$this->addTypeAliases($class);
|
||||
}
|
||||
@ -213,11 +218,11 @@ class PhpDoc
|
||||
$path .= '.md';
|
||||
if ($class instanceof FunctionDoc) {
|
||||
$functions .= "* [$line]($path)\n";
|
||||
} else if ($reflectionClass->isTrait()) {
|
||||
} elseif ($reflectionClass->isTrait()) {
|
||||
$traits .= "* [$line]($path)\n";
|
||||
} else if ($reflectionClass->isAbstract()) {
|
||||
} elseif ($reflectionClass->isAbstract()) {
|
||||
$abstract .= "* [$line]($path)\n";
|
||||
} else if ($reflectionClass->isInterface()) {
|
||||
} elseif ($reflectionClass->isInterface()) {
|
||||
$interfaces .= "* [$line]($path)\n";
|
||||
} else {
|
||||
$classes .= "* [$line]($path)\n";
|
||||
@ -233,16 +238,20 @@ class PhpDoc
|
||||
$traits = $traits ? "## Traits\n$traits" : '';
|
||||
$abstract = $abstract ? "## Abstract classes\n$abstract" : '';
|
||||
$interfaces = $interfaces ? "## Interfaces\n$interfaces" : '';
|
||||
$functions = $functions ? "## Functions\n$functions" : '';
|
||||
$classes = $classes ? "## Classes\n$classes" : '';
|
||||
|
||||
$description = explode("\n", $this->description);
|
||||
$description = $description[0] ?? '';
|
||||
|
||||
$image = $this->getImage();
|
||||
$index = <<<EOF
|
||||
---
|
||||
title: $this->title
|
||||
description: $this->description$image
|
||||
title: $this->name
|
||||
description: $description$image
|
||||
---
|
||||
# `$this->title`
|
||||
# `$this->name`
|
||||
|
||||
$description
|
||||
|
||||
$functions
|
||||
$interfaces
|
||||
@ -301,13 +310,20 @@ class PhpDoc
|
||||
if (str_ends_with($name, '[]')) {
|
||||
return $this->resolveTypeAlias($fromClass, \substr($name, 0, -2), $resolved)."[]";
|
||||
}
|
||||
$name = \rtrim(\ltrim($name, '('), ')');
|
||||
if ($name[0] === '(' && $name[strlen($name) - 1] === ')') {
|
||||
$name = $this->resolveTypeAlias($fromClass, substr($name, 1, -1), $resolved);
|
||||
return "($name)";
|
||||
}
|
||||
if (\count($split = self::splitOnWithoutParenthesis('|', $name)) > 1) {
|
||||
foreach ($split as &$name) {
|
||||
$name = $this->resolveTypeAlias($fromClass, $name, $resolved);
|
||||
}
|
||||
return \implode('|', $split);
|
||||
}
|
||||
if (str_starts_with($name, 'callable(')) {
|
||||
$name = $this->resolveTypeAlias($fromClass, substr($name, 9, -1), $resolved);
|
||||
return "callable($name)";
|
||||
}
|
||||
if (str_starts_with($name, 'array{')) {
|
||||
$new = '';
|
||||
$split = self::splitOnWithoutParenthesis(',', \substr($name, 6, -1));
|
||||
@ -426,6 +442,7 @@ class PhpDoc
|
||||
{
|
||||
$name = $class->getName();
|
||||
$fName = $this->output;
|
||||
$fName .= DIRECTORY_SEPARATOR;
|
||||
$fName .= \str_replace('\\', DIRECTORY_SEPARATOR, $name);
|
||||
$fName .= '.md';
|
||||
|
||||
|
@ -19,7 +19,11 @@
|
||||
namespace danog\PhpDoc;
|
||||
|
||||
use danog\PhpDoc\PhpDoc\PhpDoc;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Author;
|
||||
|
||||
/**
|
||||
* PHP documentation builder.
|
||||
*/
|
||||
final class PhpDocBuilder
|
||||
{
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user