1
0
mirror of https://github.com/danog/phpdoc.git synced 2024-11-26 12:04:47 +01:00
This commit is contained in:
Daniil Gentili 2020-10-15 18:09:30 +02:00
parent 864295ff3f
commit 78cb0ae5da
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
11 changed files with 281 additions and 25 deletions

1
CNAME Normal file
View File

@ -0,0 +1 @@
phpdoc.daniil.it

View File

@ -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();

View 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
View 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
View 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>

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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()."`";

View File

@ -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);
}
@ -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';

View File

@ -19,7 +19,11 @@
namespace danog\PhpDoc;
use danog\PhpDoc\PhpDoc\PhpDoc;
use phpDocumentor\Reflection\DocBlock\Tags\Author;
/**
* PHP documentation builder.
*/
final class PhpDocBuilder
{
/**