MadelineProto/src/danog/MadelineProto/PhpDocBuilder.php

239 lines
8.2 KiB
PHP

<?php
/**
* PhpDocBuilder module.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto;
use danog\MadelineProto\Async\AsyncConstruct;
use danog\MadelineProto\Db\DbPropertiesTrait;
use danog\MadelineProto\Files\Server;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\MTProtoTools\GarbageCollector;
use danog\MadelineProto\MTProtoTools\MinDatabase;
use danog\MadelineProto\MTProtoTools\PasswordCalculator;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\TL\TL;
use danog\MadelineProto\TL\TLCallback;
use danog\MadelineProto\TL\TLConstructors;
use danog\MadelineProto\TL\TLMethods;
use danog\MadelineProto\TON\ADNLConnection;
use danog\MadelineProto\TON\APIFactory as TAPIFactory;
use danog\MadelineProto\TON\InternalDoc as TInternalDoc;
use danog\MadelineProto\TON\Lite;
use HaydenPierce\ClassFinder\ClassFinder;
use phpDocumentor\Reflection\DocBlock\Tags\Author;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Property;
use phpDocumentor\Reflection\DocBlock\Tags\See;
use phpDocumentor\Reflection\DocBlockFactory;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionMethod;
class PhpDocBuilder
{
private const DISALLOW = [
AnnotationsBuilder::class,
APIFactory::class,
APIWrapper::class,
AbstractAPIFactory::class,
Bug74586Exception::class,
Connection::class,
ContextConnector::class,
DataCenter::class,
DataCenterConnection::class,
DoHConnector::class,
DocsBuilder::class,
InternalDoc::class,
Lang::class,
LightState::class,
Magic::class,
PhpDocBuilder::class,
RSA::class,
Serialization::class,
SessionPaths::class,
SettingsEmpty::class,
SettingsAbstract::class,
Snitch::class,
AsyncConstruct::class,
Server::class, // Remove when done
VoIP::class,
Crypt::class,
NothingInTheSocketException::class,
GarbageCollector::class,
MinDatabase::class,
PasswordCalculator::class,
ReferenceDatabase::class,
UpdatesState::class,
TL::class,
TLConstructors::class,
TLMethods::class,
TLCallback::class,
ADNLConnection::class,
TAPIFactory::class,
TInternalDoc::class,
Lite::class,
\ArrayIterator::class,
];
private DocBlockFactory $factory;
private string $output;
public function __construct(string $output)
{
$this->factory = DocBlockFactory::createInstance();
$this->output = $output;
}
public function run()
{
$classes = ClassFinder::getClassesInNamespace('danog\\MadelineProto', ClassFinder::RECURSIVE_MODE);
foreach ($classes as $class) {
if (\in_array($class, self::DISALLOW) || str_starts_with($class, 'danog\\MadelineProto\\Ipc')
|| str_starts_with($class, 'danog\\MadelineProto\\Loop\\Update')
|| str_starts_with($class, 'danog\\MadelineProto\\Loop\\Connection')
|| str_starts_with($class, 'danog\\MadelineProto\\MTProto\\')
|| str_starts_with($class, 'danog\\MadelineProto\\MTProtoSession\\')
|| str_starts_with($class, 'danog\\MadelineProto\\Db\\NullCache')) {
continue;
}
$class = new ReflectionClass($class);
if ($class->isTrait()) {
continue;
}
$this->generate($class);
}
$this->generate(new ReflectionClass(DbPropertiesTrait::class));
}
/**
* Create directory recursively.
*
* @param string $file
* @return string
*/
private static function createDir(string $file): string
{
$dir = \dirname($file);
if (!\file_exists($dir)) {
$this->createDir($dir);
}
return $file;
}
private function generate(ReflectionClass $class): void
{
$name = $class->getName();
$fName = $this->output;
$fName .= \str_replace(['\\', 'danog\\MadelineProto'], ['/', ''], $name);
$fName .= '.md';
$handle = \fopen(self::createDir($fName), 'w+');
$doc = $class->getDocComment();
if (!$doc) {
throw new Exception("$name has no PHPDOC!");
}
$doc = $this->factory->create($doc);
$title = $doc->getSummary();
$description = $doc->getDescription();
$tags = $doc->getTags();
$seeAlso = [];
$properties = [];
$author = new Author("Daniil Gentili", "daniil@daniil.it");
foreach ($tags as $tag) {
if ($tag instanceof Author) {
$author = $tag;
}
if ($tag instanceof Deprecated) {
return;
}
if ($tag instanceof Generic && $tag->getName() === 'internal') {
return;
}
if ($tag instanceof See) {
$seeAlso[$tag->getReference()->__toString()] = $tag->render();
}
if ($tag instanceof Property) {
$properties[$tag->getVariableName()] = [
$tag->getType(),
$tag->getDescription()
];
}
if ($tag instanceof InvalidTag && $tag->getName() === 'property') {
[$type, $description] = \explode(" $", $tag->render(), 2);
$description .= ' ';
[$varName, $description] = \explode(" ", $description, 2);
$properties[$varName] = [
\str_replace('@property ', '', $type),
$description ?? ''
];
}
}
fwrite($handle, "---\n");
fwrite($handle, "title: $name: $title\n");
fwrite($handle, "description: $description\n");
fwrite($handle, "image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png\n");
fwrite($handle, "---\n");
fwrite($handle, "# $name: $title\n");
fwrite($handle, "[Back to API index](index.md)\n\n");
fwrite($handle, "> Author: $author \n");
$constants = [];
foreach ($class->getConstants() as $key => $value) {
$refl = new ReflectionClassConstant($name, $key);
if (!$refl->isPublic()) {
continue;
}
$description = '';
if ($refl->getDocComment()) {
$docConst = $this->factory->create($refl->getDocComment());
if (\in_array($refl->getDeclaringClass()->getName(), self::DISALLOW)) {
continue;
}
$description .= $docConst->getSummary();
if ($docConst->getDescription()) {
$description .= "\n\n";
$description .= $docConst->getDescription();
}
if ($docConst->getTagsByName('internal')) {
continue;
}
}
$description = \trim($description);
$constants[$key] = [
$value,
$description
];
}
$methods = [];
foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
}
}
}