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"
},
"require-dev": {
"danog/class-finder": "dev-master",
"phpdocumentor/reflection-docblock": "^5.2",
"vlucas/phpdotenv": "^3",
"ennexa/amp-update-cache": "dev-master",
"phpunit/phpunit": "^8",
"phpunit/phpunit": "^9",
"amphp/php-cs-fixer-config": "dev-master",
"haydenpierce/class-finder": "^0.4.2",
"amphp/http-server": "dev-master",
"amphp/http": "^1.6",
"ext-ctype": "*",
@ -57,8 +57,7 @@
"danog/7to5": "^1",
"vimeo/psalm": "dev-master",
"phpstan/phpstan": "^0.12.14",
"friendsofphp/php-cs-fixer": "^2.16",
"squizlabs/php_codesniffer": "^3.5"
"friendsofphp/php-cs-fixer": "^2"
},
"suggest": {
"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
*/
@ -132,7 +132,7 @@ final class APIWrapper
}
/**
* Sleep function
* Sleep function.
*
* @return array
*/

View File

@ -55,7 +55,7 @@ class ContextConnector implements Connector
$this->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING);
return $result->getSocket();
} catch (\Throwable $e) {
if (defined('MADELINEPROTO_TEST') && \constant("MADELINEPROTO_TEST") === 'pony') {
if (\defined('MADELINEPROTO_TEST') && \constant("MADELINEPROTO_TEST") === 'pony') {
throw $e;
}
$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
*/

View File

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

View File

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

View File

@ -53,7 +53,8 @@ class Wrapper extends ClientAbstract
* @param mixed $data Payload data
* @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
{

View File

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

View File

@ -24,22 +24,25 @@ class ClassDoc extends GenericDoc
* @var array<string, MethodDoc>
*/
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();
if (!$doc) {
Logger::log($reflectionClass->getName()." has no PHPDOC");
$this->ignore = true;
return;
}
$doc = PhpDocBuilder::$factory->create($doc);
$doc = $this->builder->getFactory()->create($doc);
parent::__construct($doc);
parent::__construct($doc, $reflectionClass);
$tags = $doc->getTags();
foreach ($tags as $tag) {
if ($tag instanceof Property) {
$this->properties[$tag->getVariableName()] = new PropertyDoc(
$this->builder,
$tag->getName(),
$tag->getType(),
$tag->getDescription()
@ -52,6 +55,7 @@ class ClassDoc extends GenericDoc
$type = \str_replace('@property ', '', $type);
$description ??= '';
$this->properties[$varName] = new PropertyDoc(
$this->builder,
$varName,
$type,
$description
@ -66,8 +70,8 @@ class ClassDoc extends GenericDoc
}
$description = '';
if ($refl->getDocComment()) {
$docConst = PhpDocBuilder::$factory->create($refl->getDocComment());
if (\in_array($refl->getDeclaringClass()->getName(), PhpDocBuilder::DISALLOW)) {
$docConst = $this->builder->getFactory()->create($refl->getDocComment());
if ($this->builder->shouldIgnore($refl->getDeclaringClass()->getName())) {
continue;
}
$description .= $docConst->getSummary();
@ -88,12 +92,40 @@ class ClassDoc extends GenericDoc
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
if (str_starts_with($method->getName(), '__') && $method !== '__construct') continue;
$this->methods[$method->getName()] = new MethodDoc($method);
if (str_starts_with($method->getName(), '__') && $method !== '__construct') {
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
{
$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;
use danog\MadelineProto\PhpDocBuilder;
use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tags\Author;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
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;
abstract class GenericDoc
{
/**
* Builder instance.
*/
protected PhpDocBuilder $builder;
/**
* Name.
*/
protected string $name;
/**
* Title.
*/
private string $title;
protected string $title;
/**
* Description.
*/
private Description $description;
protected Description $description;
/**
* See also array.
*
* @var array<string, string>
* @var array<string, See>
*/
private array $seeAlso = [];
protected array $seeAlso = [];
/**
* Author.
*/
private Author $author;
protected Author $author;
/**
* Ignore this class.
*/
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->description = $doc->getDescription();
$tags = $doc->getTags();
@ -53,14 +74,65 @@ abstract class GenericDoc
break;
}
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
{
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

View File

@ -8,27 +8,37 @@ use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use ReflectionFunctionAbstract;
use ReflectionMethod;
class MethodDoc extends GenericDoc
{
private $return;
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();
if (!$doc) {
$this->ignore = true;
if ($method instanceof ReflectionMethod) {
Logger::log($method->getDeclaringClass()->getName().'::'.$method->getName().' has no PHPDOC!');
} else {
Logger::log($method->getName().' has no PHPDOC!');
}
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) {
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) {
$this->return = $tag;
} elseif ($tag instanceof Generic && $tag->getName() === 'psalm-return') {
@ -38,7 +48,7 @@ class MethodDoc extends GenericDoc
$description .= ' ';
[$varName, $description] = \explode(" ", $description, 2);
if (!$description && isset($this->params[$varName])) {
$description = $this->params[$varName]->getDescription();
$description = (string) $this->params[$varName][1];
} else {
$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;
use phpDocumentor\Reflection\DocBlock\Tags\Property;
use danog\MadelineProto\PhpDocBuilder;
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;
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\ClassFinder\ClassFinder;
use danog\MadelineProto\PhpDoc\ClassDoc;
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 danog\MadelineProto\PhpDoc\FunctionDoc;
use phpDocumentor\Reflection\DocBlockFactory;
use ReflectionClass;
use ReflectionMethod;
use ReflectionFunction;
class PhpDocBuilder
{
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,
];
public static DocBlockFactory $factory;
/**
* Namespace.
*/
private string $namespace;
/**
* Scan mode.
*/
private int $mode;
/**
* Docblock factory.
*/
private DocBlockFactory $factory;
/**
* Classes/interfaces/traits to ignore.
*
* @var ?callable
* @psalm-var null|callable(class-string)
*/
private $ignore;
/**
* Output directory.
*/
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;
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) {
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\\PhpDoc\\')
|| str_starts_with($class, 'danog\\MadelineProto\\Db\\NullCache')) {
continue;
}
$class = new ReflectionClass($class);
if ($class->isTrait()) {
$this->addTypeAliases($class);
}
foreach ($classes as $class) {
if ($this->ignore && ($this->ignore)($class)) {
continue;
}
$class = \function_exists($class)
? new ReflectionFunction($class)
: new ReflectionClass($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.
*
@ -137,25 +187,52 @@ class PhpDocBuilder
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();
$fName = $this->output;
$fName .= \str_replace(['\\', 'danog\\MadelineProto'], ['/', ''], $name);
$fName .= \str_replace('\\', DIRECTORY_SEPARATOR, $name);
$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+');
\fwrite($handle, $class);
\fclose($handle);
}
$class = new ClassDoc($class);
/*
\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");
/**
* Get docblock factory.
*
* @internal
*
* @return DocBlockFactory
*/
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
{
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;
}
$this->extra = $extra;

View File

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