1
0
This repository has been archived on 2023-11-30. You can view files and clone it, but cannot push or open issues or pull requests.
dtux__serveur-vote-lalis/include/Condorcet/Dev/CondorcetDocumentationGenerator/Generate.php

581 lines
23 KiB
PHP
Raw Normal View History

2022-09-21 12:39:11 +02:00
<?php
declare(strict_types=1);
namespace CondorcetPHP\Condorcet\Dev\CondorcetDocumentationGenerator;
use CondorcetPHP\Condorcet\Dev\CondorcetDocumentationGenerator\CondorcetDocAttributes\{Description, Example, FunctionParameter, FunctionReturn, PublicAPI, Related, Throws};
use HaydenPierce\ClassFinder\ClassFinder;
class Generate
{
// Static - Translators
public static function makeFilename(\ReflectionMethod $method): string
{
return self::getModifiersName($method).
' '.
str_replace('\\', '_', self::simpleClass($method->class)).'--'.$method->name.
'.md';
}
public static function simpleClass(string $fullClassName): string
{
return str_replace('CondorcetPHP\\Condorcet\\', '', $fullClassName);
}
public static function speakBool($c): string
{
if ($c === true || $c === 'true') {
return 'true';
}
if ($c === false || $c === 'false') {
return 'false';
}
if ($c === null || $c === 'null') {
return 'null';
}
if (\is_array($c)) {
return '['.implode(',', $c).']';
}
if (\is_object($c)) {
return 'new '.$c::class;
}
return (string) $c;
}
public static function getTypeAsString(?\ReflectionType $rf_rt, bool $codeBlock = false): ?string
{
if ($rf_rt !== null) {
if ($codeBlock) {
return '```'.((string) $rf_rt).'```';
} else {
return (string) $rf_rt;
}
}
return $rf_rt;
}
public static function getModifiersName(\ReflectionMethod $method): string
{
return implode(' ', \Reflection::getModifierNames($method->getModifiers()));
}
// Static - Builder
public static function cleverRelated(string $name): string
{
$infos = explode('::', $name);
$infos[0] = str_replace('static ', '', $infos[0]);
$url = '../'.$infos[0].' Class/public '.str_replace('::', '--', $name) . '.md';
$url = str_replace(' ', '%20', $url);
return '['.$name.']('.$url.')';
}
public static function computeRepresentationAsForIndex(\ReflectionMethod $method): string
{
return self::getModifiersName($method).
' '.
self::simpleClass($method->class).
(($method->isStatic()) ? '::' : '->').
$method->name.
' ('.(($method->getNumberOfParameters() > 0) ? '...' : '').')';
}
public static function computeRepresentationAsPHP(\ReflectionMethod $method): string
{
$option = false;
$str = '(';
$i = 0;
if ($method->getNumberOfParameters() > 0) {
foreach ($method->getParameters() as $value) {
$str .= ' ';
$str .= ($value->isOptional() && !$option) ? '[' : '';
$str .= ($i > 0) ? ', ' : '';
$str .= self::getTypeAsString($value->getType());
$str .= ' ';
$str .= $value->isPassedByReference() ? '&' : '';
$str .= '$'.$value->getName();
$str .= $value->isDefaultValueAvailable() ? ' = '.self::speakBool($value->getDefaultValue()) : '';
($value->isOptional() && !$option) ? $option = true : null;
$i++;
}
}
if ($option) {
$str .= ']';
}
$str .= ' )';
return "```php\n".
self::getModifiersName($method).' '.self::simpleClass($method->class).(($method->isStatic()) ? '::' : '->').$method->name.' '.$str. ((self::getTypeAsString($method->getReturnType()) !== null) ? ': '.self::getTypeAsString($method->getReturnType()) : '').
"\n```";
}
// Script
public function __construct(string $path)
{
$start_time = microtime(true);
$pathDirectory = $path.\DIRECTORY_SEPARATOR;
//
$FullClassList = ClassFinder::getClassesInNamespace('CondorcetPHP\Condorcet\\', ClassFinder::RECURSIVE_MODE);
$FullClassList = array_filter($FullClassList, static function (string $value) {
return (mb_strpos($value, 'Condorcet\Test') === false) && (mb_strpos($value, 'Condorcet\Dev') === false);
});
$inDoc = 0;
$non_inDoc = 0;
$total_methods = 0;
$total_nonInternal_methods = 0;
// Warnings
foreach ($FullClassList as $FullClass) {
$methods = (new \ReflectionClass($FullClass))->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($methods as $oneMethod) {
if ($oneMethod->isInternal()) {
} elseif (!empty($oneMethod->getAttributes(PublicAPI::class))) {
$inDoc++;
if ($oneMethod->getNumberOfParameters() > 0) {
foreach ($oneMethod->getParameters() as $oneParameter) {
if (empty($oneParameter->getAttributes(FunctionParameter::class))) {
var_dump('Method Has Public API attribute but parameter $'.$oneParameter->getName().' is undocumented '.$oneMethod->getDeclaringClass()->getName().'->'.$oneMethod->getName());
}
}
}
if (empty($oneMethod->getAttributes(Description::class)) && $oneMethod->getDeclaringClass()->getNamespaceName() !== '') {
var_dump('Description Attribute is empty: '.$oneMethod->getDeclaringClass()->getName().'->'.$oneMethod->getName());
}
} else {
$non_inDoc++;
if (empty($oneMethod->getAttributes(PublicAPI::class)) && $oneMethod->getDeclaringClass()->getNamespaceName() !== '') {
// var_dump('Method not has API attribute: '.$oneMethod->getDeclaringClass()->getName().'->'.$oneMethod->getName());
}
}
}
}
$full_methods_list = [];
// generate .md
foreach ($FullClassList as $FullClass) {
$reflectionClass = new \ReflectionClass($FullClass);
$methods = ($reflectionClass)->getMethods();
$shortClass = str_replace('CondorcetPHP\Condorcet\\', '', $FullClass);
$full_methods_list[$shortClass] = [
'FullClass' => $FullClass,
'shortClass' => $shortClass,
'ReflectionClass' => $reflectionClass,
'methods' => [],
];
foreach ($methods as $oneMethod) {
$method_array = $full_methods_list[$shortClass]['methods'][$oneMethod->name] = [
'FullClass' => $FullClass,
'shortClass' => $shortClass,
'name' => $oneMethod->name,
'static' => $oneMethod->isStatic(),
'visibility_public' => $oneMethod->isPublic(),
'visibility_protected' => $oneMethod->isProtected(),
'visibility_private' => $oneMethod->isPrivate(),
'ReflectionMethod' => $oneMethod,
'ReflectionClass' => $oneMethod->getDeclaringClass(),
];
$total_methods++;
if (!$oneMethod->isInternal()) {
$total_nonInternal_methods++;
}
// Write Markdown
if (!empty($apiAttribute = $oneMethod->getAttributes(PublicAPI::class)) && (empty($apiAttribute[0]->getArguments()) || \in_array(self::simpleClass($oneMethod->class), $apiAttribute[0]->getArguments(), true))) {
$path = $pathDirectory . str_replace('\\', '_', self::simpleClass($oneMethod->class)) . ' Class/';
if (!is_dir($path)) {
mkdir($path);
}
file_put_contents($path.self::makeFilename($oneMethod), $this->createMarkdownContent($oneMethod, $method_array));
}
}
}
print 'Public methods in doc: '.$inDoc.' / '.($inDoc + $non_inDoc).' | Total non-internal methods count: '.$total_nonInternal_methods.' | Number of Class: '.\count($FullClassList).' | Number of Methods including internals: '.$total_methods."\n";
// Add Index
$file_content = "> **[Presentation](../README.md) | [Manual](https://github.com/julien-boudry/Condorcet/wiki) | Methods References | [Tests](../Tests)**\n\n".
"# Public API Index*_\n".
"_*: I try to update and complete the documentation. See also [the manual](https://github.com/julien-boudry/Condorcet/wiki), [the tests](../Tests) also produce many examples. And create issues for questions or fixing documentation!_\n\n";
$file_content .= $this->makeIndex($full_methods_list);
$file_content .= "\n\n\n";
uksort($full_methods_list, 'strnatcmp');
$file_content .= "# Full Class & Methods References\n".
"_Including above methods from public API_\n\n";
$file_content .= $this->makeProfundis($full_methods_list);
// Write file
file_put_contents($pathDirectory.'README.md', $file_content);
echo 'YAH ! <br>' . (microtime(true) - $start_time) .'s';
}
// Build Methods
protected function createMarkdownContent(\ReflectionMethod $method, array $entry): string
{
// Header
$md = '## '.self::getModifiersName($method).' '. self::simpleClass($method->class).'::'.$method->name."\n\n".
"### Description \n\n".
self::computeRepresentationAsPHP($method)."\n\n".
$method->getAttributes(Description::class)[0]->getArguments()[0]."\n ";
// Input
if ($method->getNumberOfParameters() > 0) {
foreach ($method->getParameters() as $key => $value) {
if (!empty($attributes = $value->getAttributes(FunctionParameter::class))) {
$pt = $attributes[0]->newInstance()->text;
} elseif (isset($entry['input'][$value->getName()]['text'])) {
$pt = $entry['input'][$value->getName()]['text'];
} else {
$pt = '';
}
$md .= "\n\n".
'##### **'.$value->getName().':** *'.self::getTypeAsString($value->getType(), true)."* \n".
$pt." \n";
}
}
// Return Value
if (!empty($method->getAttributes(FunctionReturn::class))) {
$md .= "\n\n".
"### Return value: \n\n".
'*('.self::getTypeAsString($method->getReturnType(), true).')* '.$method->getAttributes(FunctionReturn::class)[0]->getArguments()[0]."\n\n";
}
// Throw
if (!empty($method->getAttributes(Throws::class))) {
$md .= "\n\n".
"### Throws: \n\n";
foreach ($method->getAttributes(Throws::class)[0]->getArguments() as $arg) {
$md .= '* ```'.$arg."```\n";
}
}
// Related methods
if (!empty($method->getAttributes(Related::class))) {
$md .= "\n".
"---------------------------------------\n\n".
"### Related method(s) \n\n";
foreach ($method->getAttributes(Related::class) as $RelatedAttribute) {
foreach ($RelatedAttribute->newInstance()->relatedList as $value) {
if ($value === self::simpleClass($method->class).'::'.$method->name) {
continue;
}
$md .= '* '.self::cleverRelated($value)." \n";
}
}
}
if (!empty($method->getAttributes(Example::class))) {
$md .= "\n".
"---------------------------------------\n\n".
"### Examples and explanation\n\n";
foreach ($method->getAttributes(Example::class) as $ExampleAttribute) {
$ExampleAttribute = $ExampleAttribute->newInstance();
$md .= '* **['.$ExampleAttribute->name.']('.$ExampleAttribute->link.")** \n";
}
}
return $md;
}
protected function makeIndex(array $index): string
{
$file_content = '';
$testPublicAttribute = static function (\ReflectionMethod $reflectionMethod): bool {
return !(empty($apiAttribute = $reflectionMethod->getAttributes(PublicAPI::class)) || (!empty($apiAttribute[0]->getArguments()) && !\in_array(self::simpleClass($reflectionMethod->class), $apiAttribute[0]->getArguments(), true)));
};
foreach ($index as $class => &$classMeta) {
usort($classMeta['methods'], static function (array $a, array $b): int {
if ($a['ReflectionMethod']->isStatic() === $b['ReflectionMethod']->isStatic()) {
return strnatcmp($a['ReflectionMethod']->name, $b['ReflectionMethod']->name);
} elseif ($a['ReflectionMethod']->isStatic() && !$b['ReflectionMethod']->isStatic()) {
return -1;
} else {
return 1;
}
});
$classWillBePublic = false;
if ($classMeta['ReflectionClass']->getAttributes(PublicAPI::class)) {
$classWillBePublic = true;
} else {
foreach ($classMeta['methods'] as $oneMethod) {
if ($testPublicAttribute($oneMethod['ReflectionMethod'])) {
$classWillBePublic = true;
break;
}
}
foreach ($classMeta['ReflectionClass']->getReflectionConstants() as $oneConstant) {
if (!empty($oneConstant->getAttributes(PublicAPI::class))) {
$classWillBePublic = true;
break;
}
}
foreach ($classMeta['ReflectionClass']->getProperties() as $onePropertie) {
if (!empty($onePropertie->getAttributes(PublicAPI::class))) {
$classWillBePublic = true;
break;
}
}
}
if ($classWillBePublic) {
$isEnum = enum_exists(($enumCases = $classMeta['ReflectionClass'])->name);
$file_content .= "\n";
$file_content .= '### CondorcetPHP\Condorcet\\'.$class.' '.((!$isEnum) ? 'Class' : 'Enum')." \n\n";
if ($isEnum) {
$file_content .= $this->makeEnumeCases(new \ReflectionEnum($enumCases->name), false);
$file_content .= "\n";
} else {
$file_content .= $this->makeConstants($classMeta['ReflectionClass'], \ReflectionClassConstant::IS_PUBLIC, true);
}
$file_content .= $this->makeProperties($classMeta['ReflectionClass'], null, true);
}
foreach ($classMeta['methods'] as $oneMethod) {
if (!$testPublicAttribute($oneMethod['ReflectionMethod']) || !$oneMethod['ReflectionMethod']->isUserDefined()) {
continue;
} else {
$url = str_replace('\\', '_', self::simpleClass($oneMethod['ReflectionMethod']->class)).' Class/'.self::getModifiersName($oneMethod['ReflectionMethod']).' '. str_replace('\\', '_', self::simpleClass($oneMethod['ReflectionMethod']->class).'--'. $oneMethod['ReflectionMethod']->name) . '.md';
$url = str_replace(' ', '%20', $url);
$file_content .= '* ['.self::computeRepresentationAsForIndex($oneMethod['ReflectionMethod']).']('.$url.')';
if (isset($oneMethod['ReflectionMethod']) && $oneMethod['ReflectionMethod']->hasReturnType()) {
$file_content .= ': '.self::getTypeAsString($oneMethod['ReflectionMethod']->getReturnType(), true);
}
$file_content .= " \n";
}
}
}
return $file_content;
}
protected function makeEnumeCases(\ReflectionEnum $enumReflection, bool $shortName = false): string
{
$cases = $enumReflection->getCases();
$r = '';
foreach ($cases as $oneCase) {
$name = ($shortName) ? $enumReflection->getShortName() : self::simpleClass($enumReflection->getName());
$r .= '* ```case '.$name.'::'.$oneCase->getName()."``` \n";
}
return $r;
}
protected function makeConstants(\ReflectionClass $class, ?int $type = null, bool $mustHaveApiAttribute = false, bool $addMdCodeTag = true): string
{
$file_content = '';
$hasConstants = false;
foreach ($class->getReflectionConstants($type) as $constant) {
if (!$mustHaveApiAttribute || !empty($constant->getAttributes(PublicAPI::class))) {
$file_content .= '* ';
$file_content .= $addMdCodeTag ? '```' : '';
$file_content .= $constant->isFinal() ? 'final ' : '';
$file_content .= $constant->isPublic() ? 'public' : '';
$file_content .= $constant->isProtected() ? 'protected' : '';
$file_content .= $constant->isPrivate() ? 'private' : '';
$file_content .= ' const '.$constant->getName().': ('.\gettype($constant->getValue()).')';
$file_content .= $addMdCodeTag ? '``` ' : '';
$file_content .= "\n";
$hasConstants = true;
}
}
if ($hasConstants) {
$file_content .= "\n";
}
return $file_content;
}
protected function makeProperties(\ReflectionClass $class, ?int $type = null, bool $mustHaveApiAttribute = false, bool $addMdCodeTag = true): string
{
$file_content = '';
$hasConstants = false;
foreach ($class->getProperties($type) as $propertie) {
if (!$mustHaveApiAttribute || !empty($propertie->getAttributes(PublicAPI::class))) {
$file_content .= '* ';
$file_content .= $addMdCodeTag ? '```' : '';
$file_content .= $propertie->isReadOnly() ? 'readonly ' : '';
$file_content .= $propertie->isPublic() ? 'public ' : '';
$file_content .= $propertie->isProtected() ? 'protected ' : '';
$file_content .= $propertie->isPrivate() ? 'private ' : '';
$file_content .= $propertie->isStatic() ? 'static ' : '';
$file_content .= ((string) $propertie->getType()).' $'.$propertie->getName();
$file_content .= $addMdCodeTag ? '``` ' : '';
$file_content .= "\n";
$hasConstants = true;
}
}
if ($hasConstants) {
$file_content .= "\n";
}
return $file_content;
}
protected function makeProfundis(array $index): string
{
$file_content = '';
foreach ($index as $class => &$classMeta) {
usort($classMeta['methods'], static function (array $a, array $b): int {
if ($a['static'] === $b['static']) {
if ($a['visibility_public'] && !$b['visibility_public']) {
return -1;
} elseif (!$a['visibility_public'] && $b['visibility_public']) {
return 1;
} else {
if ($a['visibility_protected'] && !$b['visibility_protected']) {
return -1;
} elseif (!$a['visibility_protected'] && $b['visibility_protected']) {
return 1;
} else {
return strnatcmp($a['name'], $b['name']);
}
}
} elseif ($a['static'] && !$b['static']) {
return -1;
} else {
return 1;
}
});
$file_content .= "\n";
$file_content .= '#### ';
$file_content .= ($classMeta['ReflectionClass']->isAbstract()) ? 'Abstract ' : '';
$file_content .= 'CondorcetPHP\Condorcet\\'.$class.' ';
$file_content .= ($p = $classMeta['ReflectionClass']->getParentClass()) ? 'extends '.$p->name.' ' : '';
$interfaces = implode(', ', $classMeta['ReflectionClass']->getInterfaceNames());
$file_content .= (!empty($interfaces)) ? 'implements '.$interfaces : '';
$file_content .= " \n";
$file_content .= "```php\n";
$isEnum = enum_exists(($enumCases = $classMeta['ReflectionClass'])->name);
if ($isEnum) {
$file_content .= $this->makeEnumeCases(new \ReflectionEnum($enumCases->name), true);
$file_content .= "\n";
} else {
$file_content .= $this->makeConstants(class: $classMeta['ReflectionClass'], addMdCodeTag: false);
}
$file_content .= $this->makeProperties(class: $classMeta['ReflectionClass'], addMdCodeTag: false);
foreach ($classMeta['methods'] as $oneMethod) {
if ($oneMethod['ReflectionMethod']->isUserDefined()) {
$parameters = $oneMethod['ReflectionMethod']->getParameters();
$parameters_string = '';
$i = 0;
foreach ($parameters as $oneP) {
$parameters_string .= (++$i > 1) ? ', ' : '';
if ($oneP->getType() !== null) {
$parameters_string .= self::getTypeAsString($oneP->getType()) . ' ';
}
$parameters_string .= '$'.$oneP->name;
if ($oneP->isDefaultValueAvailable()) {
$parameters_string .= ' = '.self::speakBool($oneP->getDefaultValue());
}
}
$representation = ($oneMethod['visibility_public']) ? 'public ' : '';
$representation .= ($oneMethod['visibility_protected']) ? 'protected ' : '';
$representation .= ($oneMethod['visibility_private']) ? 'private ' : '';
$representation .= ($oneMethod['static']) ? 'static ' : '';
$representation .= $oneMethod['name'] . ' ('.$parameters_string.')';
if ($oneMethod['ReflectionMethod']->hasReturnType()) {
$representation .= ': '.self::getTypeAsString($oneMethod['ReflectionMethod']->getReturnType());
}
$file_content .= '* '.$representation." \n";
}
}
$file_content .= "```\n";
}
return $file_content;
}
}