This commit is contained in:
Daniil Gentili 2020-10-13 10:03:35 +02:00
parent ac47a23639
commit def6199437
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
17 changed files with 416 additions and 155 deletions

View File

@ -44,12 +44,12 @@
"amphp/websocket-client": "^1.0" "amphp/websocket-client": "^1.0"
}, },
"require-dev": { "require-dev": {
"danog/class-finder": "dev-master",
"phpdocumentor/reflection-docblock": "^5.2", "phpdocumentor/reflection-docblock": "^5.2",
"vlucas/phpdotenv": "^3", "vlucas/phpdotenv": "^3",
"ennexa/amp-update-cache": "dev-master", "ennexa/amp-update-cache": "dev-master",
"phpunit/phpunit": "^8", "phpunit/phpunit": "^9",
"amphp/php-cs-fixer-config": "dev-master", "amphp/php-cs-fixer-config": "dev-master",
"haydenpierce/class-finder": "^0.4.2",
"amphp/http-server": "dev-master", "amphp/http-server": "dev-master",
"amphp/http": "^1.6", "amphp/http": "^1.6",
"ext-ctype": "*", "ext-ctype": "*",
@ -57,8 +57,7 @@
"danog/7to5": "^1", "danog/7to5": "^1",
"vimeo/psalm": "dev-master", "vimeo/psalm": "dev-master",
"phpstan/phpstan": "^0.12.14", "phpstan/phpstan": "^0.12.14",
"friendsofphp/php-cs-fixer": "^2.16", "friendsofphp/php-cs-fixer": "^2"
"squizlabs/php_codesniffer": "^3.5"
}, },
"suggest": { "suggest": {
"ext-libtgvoip": "Install the php-libtgvoip extension to make phone calls (https://github.com/danog/php-libtgvoip)" "ext-libtgvoip": "Install the php-libtgvoip extension to make phone calls (https://github.com/danog/php-libtgvoip)"

View File

@ -122,7 +122,7 @@ final class APIWrapper
} }
/** /**
* Property list * Property list.
* *
* @return array * @return array
*/ */
@ -132,7 +132,7 @@ final class APIWrapper
} }
/** /**
* Sleep function * Sleep function.
* *
* @return array * @return array
*/ */

View File

@ -55,7 +55,7 @@ class ContextConnector implements Connector
$this->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING); $this->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING);
return $result->getSocket(); return $result->getSocket();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if (defined('MADELINEPROTO_TEST') && \constant("MADELINEPROTO_TEST") === 'pony') { if (\defined('MADELINEPROTO_TEST') && \constant("MADELINEPROTO_TEST") === 'pony') {
throw $e; throw $e;
} }
$this->logger->logger('Connection failed: '.$e, \danog\MadelineProto\Logger::ERROR); $this->logger->logger('Connection failed: '.$e, \danog\MadelineProto\Logger::ERROR);

View File

@ -281,7 +281,7 @@ final class Coroutine implements Promise, \ArrayAccess, JsonSerializable
} }
/** /**
* Obtain * Obtain.
* *
* @return string * @return string
*/ */

View File

@ -146,6 +146,7 @@ class DataCenter
/** @psalm-suppress UndefinedPropertyFetch */ /** @psalm-suppress UndefinedPropertyFetch */
$array[$id]['permAuthKey']['authorized'] = $socket->authorized; $array[$id]['permAuthKey']['authorized'] = $socket->authorized;
} }
$array[$id] = [];
} }
} }
$this->setDataCenterConnections($array); $this->setDataCenterConnections($array);

View File

@ -208,10 +208,10 @@ class RedisArray extends SqlArray
return call(function () { return call(function () {
$iterator = $this->getIterator(); $iterator = $this->getIterator();
$result = []; $result = [];
$len = strlen($this->rKey('')); $len = \strlen($this->rKey(''));
while (yield $iterator->advance()) { while (yield $iterator->advance()) {
[$key, $value] = $iterator->getCurrent(); [$key, $value] = $iterator->getCurrent();
$key = substr($key, $len); $key = \substr($key, $len);
$result[$key] = $value; $result[$key] = $value;
} }
return $result; return $result;

View File

@ -53,7 +53,8 @@ class Wrapper extends ClientAbstract
* @param mixed $data Payload data * @param mixed $data Payload data
* @param SessionPaths $ipc IPC URI * @param SessionPaths $ipc IPC URI
* *
* @return \Generator<int, Promise<ChannelledSocket>|Promise<mixed>, mixed, Wrapper> * @return \Generator
* @psalm-return \Generator<int, Promise<ChannelledSocket>|Promise<mixed>, mixed, Wrapper>
*/ */
public static function create(&$data, SessionPaths $session, Logger $logger): \Generator public static function create(&$data, SessionPaths $session, Logger $logger): \Generator
{ {

View File

@ -862,9 +862,13 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->rpcLoop = new PeriodicLoopInternal($this, [$this, 'rpcReport'], 'config', 60 * 1000); $this->rpcLoop = new PeriodicLoopInternal($this, [$this, 'rpcReport'], 'config', 60 * 1000);
} }
if (!$this->ipcServer) { if (!$this->ipcServer) {
try {
$this->ipcServer = new Server($this); $this->ipcServer = new Server($this);
$this->ipcServer->setSettings($this->settings->getIpc()); $this->ipcServer->setSettings($this->settings->getIpc());
$this->ipcServer->setIpcPath($this->wrapper->session); $this->ipcServer->setIpcPath($this->wrapper->session);
} catch (\Throwable $e) {
$this->logger->logger("Error while starting IPC server: $e", Logger::FATAL_ERROR);
}
} }
$this->callCheckerLoop->start(); $this->callCheckerLoop->start();
$this->serializeLoop->start(); $this->serializeLoop->start();
@ -872,7 +876,11 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->configLoop->start(); $this->configLoop->start();
$this->checkTosLoop->start(); $this->checkTosLoop->start();
$this->rpcLoop->start(); $this->rpcLoop->start();
try {
$this->ipcServer->start(); $this->ipcServer->start();
} catch (\Throwable $e) {
$this->logger->logger("Error while starting IPC server: $e", Logger::FATAL_ERROR);
}
} }
/** /**
* Stop all internal loops. * Stop all internal loops.
@ -1167,6 +1175,9 @@ class MTProto extends AsyncConstruct implements TLCallback
*/ */
public function unreference(): void public function unreference(): void
{ {
if (!isset($this->logger)) {
$this->logger = new Logger(new \danog\MadelineProto\Settings\Logger);
}
$this->logger->logger("Will unreference instance"); $this->logger->logger("Will unreference instance");
if (isset(self::$references[\spl_object_hash($this)])) { if (isset(self::$references[\spl_object_hash($this)])) {
unset(self::$references[\spl_object_hash($this)]); unset(self::$references[\spl_object_hash($this)]);
@ -1175,6 +1186,7 @@ class MTProto extends AsyncConstruct implements TLCallback
if (isset($this->seqUpdater)) { if (isset($this->seqUpdater)) {
$this->seqUpdater->signal(true); $this->seqUpdater->signal(true);
} }
if (isset($this->channels_state)) {
$channelIds = []; $channelIds = [];
foreach ($this->channels_state->get() as $state) { foreach ($this->channels_state->get() as $state) {
$channelIds[] = $state->getChannel(); $channelIds[] = $state->getChannel();
@ -1188,9 +1200,12 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->updaters[$channelId]->signal(true); $this->updaters[$channelId]->signal(true);
} }
} }
}
if (isset($this->datacenter)) {
foreach ($this->datacenter->getDataCenterConnections() as $datacenter) { foreach ($this->datacenter->getDataCenterConnections() as $datacenter) {
$datacenter->disconnect(); $datacenter->disconnect();
} }
}
$this->logger->logger("Unreferenced instance"); $this->logger->logger("Unreferenced instance");
} }
/** /**

View File

@ -24,22 +24,25 @@ class ClassDoc extends GenericDoc
* @var array<string, MethodDoc> * @var array<string, MethodDoc>
*/ */
private array $methods = []; private array $methods = [];
public function __construct(ReflectionClass $reflectionClass) public function __construct(PhpDocBuilder $builder, ReflectionClass $reflectionClass)
{ {
$this->builder = $builder;
$this->name = $reflectionClass->getName();
$doc = $reflectionClass->getDocComment(); $doc = $reflectionClass->getDocComment();
if (!$doc) { if (!$doc) {
Logger::log($reflectionClass->getName()." has no PHPDOC"); Logger::log($reflectionClass->getName()." has no PHPDOC");
$this->ignore = true; $this->ignore = true;
return; return;
} }
$doc = PhpDocBuilder::$factory->create($doc); $doc = $this->builder->getFactory()->create($doc);
parent::__construct($doc); parent::__construct($doc, $reflectionClass);
$tags = $doc->getTags(); $tags = $doc->getTags();
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ($tag instanceof Property) { if ($tag instanceof Property) {
$this->properties[$tag->getVariableName()] = new PropertyDoc( $this->properties[$tag->getVariableName()] = new PropertyDoc(
$this->builder,
$tag->getName(), $tag->getName(),
$tag->getType(), $tag->getType(),
$tag->getDescription() $tag->getDescription()
@ -52,6 +55,7 @@ class ClassDoc extends GenericDoc
$type = \str_replace('@property ', '', $type); $type = \str_replace('@property ', '', $type);
$description ??= ''; $description ??= '';
$this->properties[$varName] = new PropertyDoc( $this->properties[$varName] = new PropertyDoc(
$this->builder,
$varName, $varName,
$type, $type,
$description $description
@ -66,8 +70,8 @@ class ClassDoc extends GenericDoc
} }
$description = ''; $description = '';
if ($refl->getDocComment()) { if ($refl->getDocComment()) {
$docConst = PhpDocBuilder::$factory->create($refl->getDocComment()); $docConst = $this->builder->getFactory()->create($refl->getDocComment());
if (\in_array($refl->getDeclaringClass()->getName(), PhpDocBuilder::DISALLOW)) { if ($this->builder->shouldIgnore($refl->getDeclaringClass()->getName())) {
continue; continue;
} }
$description .= $docConst->getSummary(); $description .= $docConst->getSummary();
@ -88,12 +92,40 @@ class ClassDoc extends GenericDoc
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
if (str_starts_with($method->getName(), '__') && $method !== '__construct') continue; if (str_starts_with($method->getName(), '__') && $method !== '__construct') {
$this->methods[$method->getName()] = new MethodDoc($method); continue;
} }
$this->methods[$method->getName()] = new MethodDoc($this->builder, $method);
}
$this->methods = \array_filter($this->methods, fn (MethodDoc $doc): bool => !$doc->shouldIgnore());
//$this->properties = \array_filter($this->properties, fn (PropertyDoc $doc): bool => !$doc->shouldIgnore());
} }
public function format(): string public function format(): string
{ {
$init = parent::format();
$methods = '';
$properties = '';
if ($this->methods) {
$init .= "\n";
foreach ($this->methods as $method) {
$init .= "* ".$method->getSignature()."\n";
}
$init .= "\n";
$init .= "## Methods:\n$methods\n";
foreach ($this->methods as $method) {
$init .= $method->format();
$init .= "\n";
}
}
if ($properties) {
$init .= "## Properties:\n$properties\n";
/*foreach ($this->properties as $property) {
$init .= $property->format();
$init .= "\n";
}*/
}
return $init;
} }
} }

View File

@ -0,0 +1,25 @@
<?php
namespace danog\MadelineProto\PhpDoc;
use danog\MadelineProto\Logger;
use danog\MadelineProto\PhpDocBuilder;
use ReflectionFunction;
class FunctionDoc extends MethodDoc
{
public function __construct(PhpDocBuilder $builder, ReflectionFunction $reflectionClass)
{
$this->builder = $builder;
$this->nameGenericDoc = $reflectionClass->getName();
$doc = $reflectionClass->getDocComment();
if (!$doc) {
Logger::log($reflectionClass->getName()." has no PHPDOC");
$this->ignore = true;
return;
}
$doc = $this->builder->getFactory()->create($doc);
parent::__construct($builder, $reflectionClass);
}
}

View File

@ -2,39 +2,60 @@
namespace danog\MadelineProto\PhpDoc; namespace danog\MadelineProto\PhpDoc;
use danog\MadelineProto\PhpDocBuilder;
use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tags\Author; use phpDocumentor\Reflection\DocBlock\Tags\Author;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
use phpDocumentor\Reflection\DocBlock\Tags\See; use phpDocumentor\Reflection\DocBlock\Tags\See;
abstract class GenericDoc abstract class GenericDoc
{ {
/**
* Builder instance.
*/
protected PhpDocBuilder $builder;
/**
* Name.
*/
protected string $name;
/** /**
* Title. * Title.
*/ */
private string $title; protected string $title;
/** /**
* Description. * Description.
*/ */
private Description $description; protected Description $description;
/** /**
* See also array. * See also array.
* *
* @var array<string, string> * @var array<string, See>
*/ */
private array $seeAlso = []; protected array $seeAlso = [];
/** /**
* Author. * Author.
*/ */
private Author $author; protected Author $author;
/** /**
* Ignore this class. * Ignore this class.
*/ */
protected bool $ignore = false; protected bool $ignore = false;
public function __construct(DocBlock $doc) /**
* Class name.
*/
protected string $className;
/**
* Class namespace.
*/
protected string $namespace;
public function __construct(DocBlock $doc, $reflectionClass)
{ {
$this->className = $reflectionClass->getName();
$this->namespace = \str_replace('/', '\\', \dirname(\str_replace('\\', '/', $this->className)));
$this->title = $doc->getSummary(); $this->title = $doc->getSummary();
$this->description = $doc->getDescription(); $this->description = $doc->getDescription();
$tags = $doc->getTags(); $tags = $doc->getTags();
@ -53,14 +74,65 @@ abstract class GenericDoc
break; break;
} }
if ($tag instanceof See) { if ($tag instanceof See) {
$this->seeAlso[$tag->getReference()->__toString()] = $tag->render(); $this->seeAlso[$tag->getReference()->__toString()] = $tag;
} }
} }
} }
public function seeAlso(): string
{
$seeAlso = '';
foreach ($this->seeAlso as $see) {
$ref = $see->getReference();
if ($ref instanceof Fqsen) {
$ref = (string) $ref;
$ref = $this->builder->resolveTypeAlias($this->className, $ref);
$refExpl = \explode("\\", $ref);
$name = \array_pop($refExpl);
$namespace = \explode('/', $this->namespace);
$count = \count($refExpl);
foreach ($namespace as $k => $name) {
if (isset($refExpl[$k]) && $refExpl[$k] === $name) {
$count--;
} else {
break;
}
}
$ref = \str_repeat('../', $count)
.\implode('/', \array_slice($refExpl, $count))
."/$name.md";
$desc = $see->getDescription() ?: $ref;
$seeAlso .= "* [$desc]($ref)\n";
}
if ($ref instanceof Url) {
$desc = $see->getDescription() ?: $ref;
$seeAlso .= "* [$desc]($ref)\n";
}
}
if ($seeAlso) {
$seeAlso = "\n#### See also: \n$seeAlso\n\n";
}
return $seeAlso;
}
public function format(): string public function format(): string
{ {
return ''; $seeAlso = $this->seeAlso();
return <<<EOF
---
title: $this->name: $this->title
description: $this->description
image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
---
# `$this->name`: $this->title
[Back to API index](index.md)
> Author: $this->author
$this->description
$seeAlso
EOF;
} }
public function shouldIgnore(): bool public function shouldIgnore(): bool

View File

@ -8,27 +8,37 @@ use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlock\Tags\Return_; use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use ReflectionFunctionAbstract;
use ReflectionMethod; use ReflectionMethod;
class MethodDoc extends GenericDoc class MethodDoc extends GenericDoc
{ {
private $return; private $return;
private array $params = []; private array $params = [];
public function __construct(ReflectionMethod $method) public function __construct(PhpDocBuilder $phpDocBuilder, ReflectionFunctionAbstract $method)
{ {
$this->builder = $phpDocBuilder;
$this->name = $method->getName();
$doc = $method->getDocComment(); $doc = $method->getDocComment();
if (!$doc) { if (!$doc) {
$this->ignore = true; $this->ignore = true;
if ($method instanceof ReflectionMethod) {
Logger::log($method->getDeclaringClass()->getName().'::'.$method->getName().' has no PHPDOC!'); Logger::log($method->getDeclaringClass()->getName().'::'.$method->getName().' has no PHPDOC!');
} else {
Logger::log($method->getName().' has no PHPDOC!');
}
return; return;
} }
$doc = PhpDocBuilder::$factory->create($doc); $doc = $this->builder->getFactory()->create($doc);
parent::__construct($doc); parent::__construct($doc, $method instanceof ReflectionMethod ? $method->getDeclaringClass() : $method);
foreach ($doc->getTags() as $tag) { foreach ($doc->getTags() as $tag) {
if ($tag instanceof Param && !isset($this->params[$tag->getVariableName()])) { if ($tag instanceof Param && !isset($this->params[$tag->getVariableName()])) {
$this->params[$tag->getVariableName()] = $tag; $this->params[$tag->getVariableName()] = [
$tag->getType(),
$tag->getDescription()
];
} elseif ($tag instanceof Return_ && !$this->return) { } elseif ($tag instanceof Return_ && !$this->return) {
$this->return = $tag; $this->return = $tag;
} elseif ($tag instanceof Generic && $tag->getName() === 'psalm-return') { } elseif ($tag instanceof Generic && $tag->getName() === 'psalm-return') {
@ -38,7 +48,7 @@ class MethodDoc extends GenericDoc
$description .= ' '; $description .= ' ';
[$varName, $description] = \explode(" ", $description, 2); [$varName, $description] = \explode(" ", $description, 2);
if (!$description && isset($this->params[$varName])) { if (!$description && isset($this->params[$varName])) {
$description = $this->params[$varName]->getDescription(); $description = (string) $this->params[$varName][1];
} else { } else {
$description = new Description($description); $description = new Description($description);
} }
@ -50,8 +60,36 @@ class MethodDoc extends GenericDoc
} }
} }
public function render(): string public function getSignature(): string
{ {
return ''; $sig = $this->name;
$sig .= "(";
foreach ($this->params as $var => [$type, $description]) {
$sig .= $type.' ';
$sig .= "$".$var;
$sig .= ', ';
}
$sig = \trim($sig, ', ');
$sig .= ')';
if ($this->return) {
$sig .= ': ';
$sig .= $this->return;
}
return $sig;
}
public function format(): string
{
$sig = '### '.$this->getSignature();
$sig .= "\n\n";
$sig .= $this->title;
$sig .= "\n";
$sig .= $this->description;
$sig .= "\n";
if ($this->return && $this->return->getDescription() && $this->return->getDescription()->render()) {
$sig .= "\nReturn value: ".$this->return->getDescription()."\n";
}
$sig .= $this->seeAlso();
$sig .= "\n";
return $sig;
} }
} }

View File

@ -2,11 +2,11 @@
namespace danog\MadelineProto\PhpDoc; namespace danog\MadelineProto\PhpDoc;
use phpDocumentor\Reflection\DocBlock\Tags\Property; use danog\MadelineProto\PhpDocBuilder;
class PropertyDoc class PropertyDoc
{ {
public function __construct(string $name, $type, $description) public function __construct(PhpDocBuilder $phpDocBuilder, string $name, $type, $description)
{ {
} }

View File

@ -18,109 +18,159 @@
namespace danog\MadelineProto; namespace danog\MadelineProto;
use danog\MadelineProto\Async\AsyncConstruct; use danog\ClassFinder\ClassFinder;
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\PhpDoc\ClassDoc; use danog\MadelineProto\PhpDoc\ClassDoc;
use danog\MadelineProto\TL\TL; use danog\MadelineProto\PhpDoc\FunctionDoc;
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\DocBlockFactory; use phpDocumentor\Reflection\DocBlockFactory;
use ReflectionClass; use ReflectionClass;
use ReflectionMethod; use ReflectionFunction;
class PhpDocBuilder class PhpDocBuilder
{ {
const DISALLOW = [ /**
AnnotationsBuilder::class, * Namespace.
APIFactory::class, */
APIWrapper::class, private string $namespace;
AbstractAPIFactory::class, /**
Bug74586Exception::class, * Scan mode.
Connection::class, */
ContextConnector::class, private int $mode;
DataCenter::class, /**
DataCenterConnection::class, * Docblock factory.
DoHConnector::class, */
DocsBuilder::class, private DocBlockFactory $factory;
InternalDoc::class, /**
Lang::class, * Classes/interfaces/traits to ignore.
LightState::class, *
Magic::class, * @var ?callable
PhpDocBuilder::class, * @psalm-var null|callable(class-string)
RSA::class, */
Serialization::class, private $ignore;
SessionPaths::class, /**
SettingsEmpty::class, * Output directory.
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,
];
public static DocBlockFactory $factory;
private string $output; private string $output;
public function __construct(string $output) /**
* Use map.
*
* array<class-string, array<class-string, class-string>>
*/
private array $useMap = [];
/**
* Create docblock builder.
*
* @param string $namespace Namespace
* @param int $mode Finder mode, an OR-selection of ClassFinder::ALLOW_*
*
* @return self
*/
public static function fromNamespace(string $namespace, int $mode = ClassFinder::ALLOW_ALL): self
{
return new self($namespace, $mode);
}
/**
* Constructor.
*
* @param string $namespace
* @param int $mode
*/
private function __construct(string $namespace, int $mode)
{
$this->factory = DocBlockFactory::createInstance();
$this->namespace = $namespace;
$this->mode = $mode;
}
/**
* Set filter to ignore certain classes.
*
* @param callable $ignore
*
* @psalm-param callable(class-string) $ignore
*
* @return self
*/
public function setFilter(callable $ignore): self
{
$this->ignore = $ignore;
return $this;
}
/**
* Set output directory.
*
* @param string $output Output directory
*
* @return self
*/
public function setOutput(string $output): self
{ {
self::$factory = DocBlockFactory::createInstance();
$this->output = $output; $this->output = $output;
return $this;
} }
public function run() /**
* Run documentor.
*
* @return self
*/
public function run(): self
{ {
$classes = ClassFinder::getClassesInNamespace('danog\\MadelineProto', ClassFinder::RECURSIVE_MODE); $classes = ClassFinder::getClassesInNamespace($this->namespace, $this->mode | ClassFinder::RECURSIVE_MODE);
foreach ($classes as $class) { foreach ($classes as $class) {
if (\in_array($class, self::DISALLOW) || str_starts_with($class, 'danog\\MadelineProto\\Ipc') $this->addTypeAliases($class);
|| str_starts_with($class, 'danog\\MadelineProto\\Loop\\Update') }
|| str_starts_with($class, 'danog\\MadelineProto\\Loop\\Connection') foreach ($classes as $class) {
|| str_starts_with($class, 'danog\\MadelineProto\\MTProto\\') if ($this->ignore && ($this->ignore)($class)) {
|| str_starts_with($class, 'danog\\MadelineProto\\MTProtoSession\\')
|| str_starts_with($class, 'danog\\MadelineProto\\PhpDoc\\')
|| str_starts_with($class, 'danog\\MadelineProto\\Db\\NullCache')) {
continue;
}
$class = new ReflectionClass($class);
if ($class->isTrait()) {
continue; continue;
} }
$class = \function_exists($class)
? new ReflectionFunction($class)
: new ReflectionClass($class);
$this->generate($class); $this->generate($class);
} }
$this->generate(new ReflectionClass(DbPropertiesTrait::class));
}
return $this;
}
/**
* Resolve type alias.
*
* @internal
*
* @param string $fromClass Class from where this function is called
* @param string $name Name to resolve
*
* @psalm-param class-string $fromClass Class from where this function is called
* @psalm-param class-string $name Name to resolve
*
* @return string
*/
public function resolveTypeAlias(string $fromClass, string $name): string
{
return $this->useMap[$fromClass][$name] ?? $name;
}
/**
* Add type alias.
*
* @param string $class
*
* @psalm-param class-string $class
*
* @return void
*/
private function addTypeAliases(string $class)
{
$reflectionClass = \function_exists($class)
? new ReflectionFunction($class)
: new ReflectionClass($class);
$payload = \file_get_contents($reflectionClass->getFileName());
\preg_match_all("/use *(function)? +(.*?)(?: +as +(.+))? *;/", $payload, $matches, PREG_SET_ORDER|PREG_UNMATCHED_AS_NULL);
foreach ($matches as [, $function, $import, $alias]) {
$import = "\\$import";
$alias ??= \basename(\str_replace('\\', '/', $import));
$this->useMap[$class][$alias] = $import;
$this->useMap[$class]['\\'.$alias] = $import;
}
}
/** /**
* Create directory recursively. * Create directory recursively.
* *
@ -137,25 +187,52 @@ class PhpDocBuilder
return $file; return $file;
} }
private function generate(ReflectionClass $class): void /**
* Generate documentation for class.
*
* @param ReflectionClass|ReflectionFunction $class Class
*
* @return void
*/
private function generate($class): void
{ {
$name = $class->getName(); $name = $class->getName();
$fName = $this->output; $fName = $this->output;
$fName .= \str_replace(['\\', 'danog\\MadelineProto'], ['/', ''], $name); $fName .= \str_replace('\\', DIRECTORY_SEPARATOR, $name);
$fName .= '.md'; $fName .= '.md';
$class = $class instanceof ReflectionFunction
? new FunctionDoc($this, $class)
: new ClassDoc($this, $class);
if ($class->shouldIgnore()) {
return;
}
$class = $class->format();
$handle = \fopen(self::createDir($fName), 'w+'); $handle = \fopen(self::createDir($fName), 'w+');
\fwrite($handle, $class);
\fclose($handle);
}
$class = new ClassDoc($class); /**
/* * Get docblock factory.
\fwrite($handle, "---\n"); *
\fwrite($handle, "title: $name: $title\n"); * @internal
\fwrite($handle, "description: $description\n"); *
\fwrite($handle, "image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png\n"); * @return DocBlockFactory
\fwrite($handle, "---\n");
\fwrite($handle, "# $name: $title\n");
\fwrite($handle, "[Back to API index](index.md)\n\n");
\fwrite($handle, "> Author: $author \n");
*/ */
public function getFactory(): DocBlockFactory
{
return $this->factory;
}
/**
* Whether should ignore trait/class/interface.
*
* @return bool
*/
public function shouldIgnore(string $class): bool
{
return ($this->ignore)($class);
} }
} }

View File

@ -1 +0,0 @@
<?php

View File

@ -170,7 +170,9 @@ class Logger extends SettingsAbstract
public function setExtra($extra): self public function setExtra($extra): self
{ {
if ($this->type === MadelineProtoLogger::CALLABLE_LOGGER && !\is_callable($extra)) { if ($this->type === MadelineProtoLogger::CALLABLE_LOGGER && !\is_callable($extra)) {
$this->setType(MadelineProtoLogger::NO_LOGGER); $this->setType((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
? MadelineProtoLogger::ECHO_LOGGER
: MadelineProtoLogger::FILE_LOGGER);
return $this; return $this;
} }
$this->extra = $extra; $this->extra = $extra;

View File

@ -8,7 +8,7 @@ use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools; use danog\MadelineProto\Tools;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
\define("MADELINEPROTO_TEST", "pony"); \define('MADELINEPROTO_TEST', 'pony');
final class DataCenterTest extends TestCase final class DataCenterTest extends TestCase
{ {