mirror of
https://github.com/Sysbot-org/tgscraper.git
synced 2024-11-23 03:56:48 +01:00
Major refactoring, should be at least stable now
This commit is contained in:
parent
ee8f6009ff
commit
7a92b63cc6
81
.gitignore
vendored
Normal file
81
.gitignore
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
### Composer template
|
||||
composer.phar
|
||||
/vendor/
|
||||
|
||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
# composer.lock
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
21
README.md
21
README.md
@ -1,2 +1,19 @@
|
||||
# tgbotapi-generator
|
||||
A PHP framework used to extract bot API schema (and maybe to generate PHP classes from it).
|
||||
# TGScraper
|
||||
A PHP library used to extract JSON data (and auto-generate PHP classes) from [Telegram bot API documentation page](https://core.telegram.org/bots/api).
|
||||
|
||||
**Note: the scraper is, obviously, based on a hack and you shouldn't rely on automagically generated files from it, since they are prone to errors. I'll try to fix them ASAP, but manual review is always required (at least for now).**
|
||||
|
||||
## Installation
|
||||
|
||||
Install the library with composer:
|
||||
|
||||
```bash
|
||||
$ composer require sysbot/tgscraper
|
||||
```
|
||||
|
||||
## Using from command line
|
||||
|
||||
Once installed, you can use the CLI to interact with the library:
|
||||
```bash
|
||||
$ vendor/bin/tgscraper
|
||||
```
|
95
bin/tgscraper
Normal file
95
bin/tgscraper
Normal file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env php8.0
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use TgScraper\Generator;
|
||||
|
||||
$args = getopt('o:u::n::h::');
|
||||
|
||||
if (array_key_exists('h', $args)) {
|
||||
echo 'Usage: tgscraper -o <output> [-u <url>] [-n <namespace>] [-s <scheme>]' . PHP_EOL . PHP_EOL;
|
||||
echo 'Options:' . PHP_EOL;
|
||||
echo ' -n <namespace> namespace to use (for PHP stubs only)' . PHP_EOL;
|
||||
echo ' -o <output> output file/directory (the directory must exist) for JSON schema or PHP stubs' . PHP_EOL;
|
||||
echo ' -s <scheme> path to custom scheme to use (for PHP stubs only)' . PHP_EOL;
|
||||
echo ' -u <url> URL to use for fetching bot API data' . PHP_EOL . PHP_EOL;
|
||||
echo 'Note: based on the "-o" value, the script will automatically detect whether to output a JSON schema or the PHP stubs.' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
|
||||
$output = $args['o'] ?? null;
|
||||
$namespace = $args['n'] ?? 'Generated';
|
||||
$url = $args['u'] ?? Generator::BOT_API_URL;
|
||||
$scheme = $args['s'] ?? null;
|
||||
|
||||
if (empty($output)) {
|
||||
fwrite(STDERR, 'ERROR: "-o" option missing!' . PHP_EOL . PHP_EOL);
|
||||
fwrite(STDERR, 'Use "tgscraper -h" for help.' . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!empty($url) and !filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
echo '> WARNING: URL not valid, the default one will be used.' . PHP_EOL;
|
||||
$url = Generator::BOT_API_URL;
|
||||
}
|
||||
|
||||
$generator = new Generator($url);
|
||||
$data = null;
|
||||
if (is_dir($output)) {
|
||||
if (!empty($scheme)) {
|
||||
$data = file_get_contents($scheme);
|
||||
if (false === $data) {
|
||||
echo sprintf('> WARNING: Cannot read data from "%s", URL will be used instead.%s', $scheme, PHP_EOL);
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
try {
|
||||
echo '> Extracting JSON scheme.' . PHP_EOL;
|
||||
$data = $generator->toJson();
|
||||
} catch (Exception $e) {
|
||||
echo '> ERROR: Unable to extract scheme:' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
echo sprintf('> Loaded data from "%s".%s', $scheme, PHP_EOL);
|
||||
}
|
||||
echo sprintf('> Outputting PHP stubs to %s.%s', realpath($output), PHP_EOL);
|
||||
$result = $generator->toStubs($output, $namespace, $data);
|
||||
if (!$result) {
|
||||
echo sprintf('> ERROR: unable to create stubs.%s', PHP_EOL);
|
||||
}
|
||||
exit(!$result);
|
||||
}
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
try {
|
||||
echo '> Extracting JSON scheme.' . PHP_EOL;
|
||||
$data = $generator->toJson();
|
||||
} catch (Exception $e) {
|
||||
echo '> ERROR: Unable to extract scheme:' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
echo sprintf('> Outputting PHP stubs to %s.%s', realpath($output), PHP_EOL);
|
||||
$result = $generator->toStubs($output, $namespace, $data);
|
||||
if (!$result) {
|
||||
echo '> ERROR: unable to create stubs.%s' . PHP_EOL;
|
||||
}
|
||||
exit(!$result);
|
||||
}
|
||||
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
touch($output);
|
||||
echo sprintf('> Outputting JSON schema to %s.%s', realpath($output), PHP_EOL);
|
||||
|
||||
try {
|
||||
$data = $generator->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
echo '> ERROR: Unable to generate scheme:' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$error = file_put_contents($output, $data) === false;
|
||||
|
||||
if ($error) {
|
||||
echo '> ERROR: unable to write to file.' . PHP_EOL;
|
||||
}
|
||||
|
||||
exit($error);
|
@ -1,22 +1,29 @@
|
||||
{
|
||||
"name": "sys-001/tgbotapi-gen",
|
||||
"name": "sysbot/tgscraper",
|
||||
"description": "Utility to extract scheme from Telegram Bot API webpage.",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*",
|
||||
"nette/php-generator": "^3.5",
|
||||
"paquettg/php-html-parser": "^2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"TGBotApi\\": "src/"
|
||||
"TgScraper\\": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"ext-json": "*",
|
||||
"nette/php-generator": "^3.3",
|
||||
"paquettg/php-html-parser": "^2.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/tgscraper"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "sys-001",
|
||||
"email": "honfu7891@etlgr.com"
|
||||
"email": "sys@sys001.ml",
|
||||
"homepage": "https://sys001.ml",
|
||||
"role": "Developer"
|
||||
}
|
||||
]
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Sysbot-org/tgscraper/issues"
|
||||
}
|
||||
}
|
||||
|
66
composer.lock
generated
66
composer.lock
generated
@ -4,35 +4,39 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e0c67734f3ab978c6aebf8a35453ba15",
|
||||
"content-hash": "bb4b87080b33e528f8e53f4c0a4568cd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "nette/php-generator",
|
||||
"version": "v3.3.3",
|
||||
"version": "v3.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/php-generator.git",
|
||||
"reference": "a4ff22c91681fefaa774cf952a2b69c2ec9477c1"
|
||||
"reference": "119f01a7bd590469cb01b538f20a125a28853626"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/a4ff22c91681fefaa774cf952a2b69c2ec9477c1",
|
||||
"reference": "a4ff22c91681fefaa774cf952a2b69c2ec9477c1",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/119f01a7bd590469cb01b538f20a125a28853626",
|
||||
"reference": "119f01a7bd590469cb01b538f20a125a28853626",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nette/utils": "^2.4.2 || ^3.0",
|
||||
"nette/utils": "^3.1.2",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.0",
|
||||
"nikic/php-parser": "^4.4",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"nikic/php-parser": "to use ClassType::withBodiesFrom() & GlobalFunction::withBodyFrom()"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
"dev-master": "3.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -64,24 +68,31 @@
|
||||
"php",
|
||||
"scaffolding"
|
||||
],
|
||||
"time": "2020-01-20T11:40:42+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/php-generator/issues",
|
||||
"source": "https://github.com/nette/php-generator/tree/v3.5.3"
|
||||
},
|
||||
"time": "2021-02-24T18:40:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "d6cd63d77dd9a85c3a2fae707e1255e44c2bc182"
|
||||
"reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/d6cd63d77dd9a85c3a2fae707e1255e44c2bc182",
|
||||
"reference": "d6cd63d77dd9a85c3a2fae707e1255e44c2bc182",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/967cfc4f9a1acd5f1058d76715a424c53343c20c",
|
||||
"reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2 <8.1"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
@ -90,7 +101,7 @@
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use Image",
|
||||
"ext-iconv": "to use Strings::webalize() and toAscii()",
|
||||
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
|
||||
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||
"ext-json": "to use Nette\\Utils\\Json",
|
||||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
@ -100,7 +111,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
"dev-master": "3.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -111,8 +122,8 @@
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause",
|
||||
"GPL-2.0",
|
||||
"GPL-3.0"
|
||||
"GPL-2.0-only",
|
||||
"GPL-3.0-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
@ -124,7 +135,7 @@
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
|
||||
"description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
|
||||
"homepage": "https://nette.org",
|
||||
"keywords": [
|
||||
"array",
|
||||
@ -142,7 +153,11 @@
|
||||
"utility",
|
||||
"validation"
|
||||
],
|
||||
"time": "2020-01-03T18:13:31+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v3.2.2"
|
||||
},
|
||||
"time": "2021-03-03T22:53:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paquettg/php-html-parser",
|
||||
@ -196,6 +211,10 @@
|
||||
"html",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paquettg/php-html-parser/issues",
|
||||
"source": "https://github.com/paquettg/php-html-parser/tree/2.2.1"
|
||||
},
|
||||
"time": "2020-01-20T12:59:15+00:00"
|
||||
},
|
||||
{
|
||||
@ -242,6 +261,10 @@
|
||||
"encoding",
|
||||
"string"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paquettg/string-encoder/issues",
|
||||
"source": "https://github.com/paquettg/string-encoder/tree/1.0.1"
|
||||
},
|
||||
"time": "2018-12-21T02:25:09+00:00"
|
||||
}
|
||||
],
|
||||
@ -252,8 +275,9 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.2",
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
|
7
main.php
7
main.php
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
|
||||
$json_scheme = TGBotApi\Generator::toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
echo $json_scheme;
|
@ -1,101 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace TGBotApi;
|
||||
namespace TgScraper;
|
||||
|
||||
use Exception;
|
||||
use JsonException;
|
||||
use PHPHtmlParser\Dom;
|
||||
use PHPHtmlParser\Exceptions\{ChildNotFoundException,
|
||||
CircularException,
|
||||
CurlException,
|
||||
NotLoadedException,
|
||||
ParentNotFoundException,
|
||||
StrictException
|
||||
};
|
||||
|
||||
class Generator
|
||||
{
|
||||
|
||||
private const EMPTY_FIELDS = [
|
||||
'getWebhookInfo',
|
||||
'getMe',
|
||||
'InputFile',
|
||||
'InputMedia',
|
||||
'InlineQueryResult',
|
||||
'InputMessageContent',
|
||||
'PassportElementError',
|
||||
'CallbackGame',
|
||||
'getMyCommands',
|
||||
'logOut',
|
||||
'close'
|
||||
];
|
||||
|
||||
private const BOOL_RETURNS = [
|
||||
'answerShippingQuery',
|
||||
'answerPreCheckoutQuery'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $target_directory
|
||||
* @param string $namespace_prefix
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function toClasses(string $target_directory = '', string $namespace_prefix = ''): bool
|
||||
public const BOT_API_URL = 'https://core.telegram.org/bots/api';
|
||||
|
||||
public function __construct(private string $url = self::BOT_API_URL)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param string $namespace
|
||||
* @param string|null $scheme
|
||||
* @return bool
|
||||
*/
|
||||
public function toStubs(string $directory = '', string $namespace = '', string $scheme = null): bool
|
||||
{
|
||||
$target_directory = self::getTargetDirectory($target_directory);
|
||||
mkdir($target_directory . '/Methods', 0755);
|
||||
mkdir($target_directory . '/Types', 0755);
|
||||
try {
|
||||
$stub_provider = new StubProvider($namespace_prefix);
|
||||
$code = $stub_provider->generateCode(self::extractScheme());
|
||||
foreach ($code['methods'] as $class_name => $method) {
|
||||
file_put_contents($target_directory . '/Methods/' . $class_name . '.php', $method);
|
||||
$directory = self::getTargetDirectory($directory);
|
||||
} catch (Exception $e) {
|
||||
echo 'Unable to use target directory:' . $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
mkdir($directory . '/Types', 0755);
|
||||
try {
|
||||
if (!empty($scheme)) {
|
||||
try {
|
||||
$data = json_decode($scheme, true, flags: JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
$data = null;
|
||||
}
|
||||
}
|
||||
$data = $data ?? self::extractScheme();
|
||||
$creator = new StubCreator($data, $namespace);
|
||||
$code = $creator->generateCode();
|
||||
foreach ($code['types'] as $class_name => $type) {
|
||||
file_put_contents($target_directory . '/Types/' . $class_name . '.php', $type);
|
||||
$filename = sprintf('%s/Types/%s.php', $directory, $class_name);
|
||||
file_put_contents($filename, $type);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
file_put_contents($directory . '/API.php', $code['api']);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $target_directory
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function getTargetDirectory(string $target_directory): string
|
||||
{
|
||||
mkdir($target_directory, 0755);
|
||||
$target_directory = realpath($target_directory);
|
||||
if (false == $target_directory) {
|
||||
$target_directory = __DIR__ . '/generated';
|
||||
if (!file_exists($target_directory)) {
|
||||
mkdir($target_directory, 0755);
|
||||
$target_path = realpath($target_directory);
|
||||
if (false == $target_path) {
|
||||
if (!mkdir($target_directory)) {
|
||||
$target_path = __DIR__ . '/../generated';
|
||||
if (!file_exists($target_path)) {
|
||||
mkdir($target_path, 0755);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (realpath($target_path) == false) {
|
||||
throw new Exception('Could not create target directory');
|
||||
}
|
||||
return $target_directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \PHPHtmlParser\Exceptions\ChildNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\CircularException
|
||||
* @throws \PHPHtmlParser\Exceptions\CurlException
|
||||
* @throws \PHPHtmlParser\Exceptions\NotLoadedException
|
||||
* @throws \PHPHtmlParser\Exceptions\ParentNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\StrictException
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws CurlException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
*/
|
||||
private static function extractScheme(): array
|
||||
private function extractScheme(): array
|
||||
{
|
||||
$dom = new Dom;
|
||||
$dom->loadFromURL('https://core.telegram.org/bots/api');
|
||||
$dom->loadFromURL($this->url);
|
||||
$elements = $dom->find('h4');
|
||||
$i = 0;
|
||||
$data = [];
|
||||
/* @var Dom\AbstractNode $element */
|
||||
foreach ($elements as $element) {
|
||||
if (false === strpos($name = $element->text, ' ')) {
|
||||
if (!str_contains($name = $element->text, ' ')) {
|
||||
$is_method = self::isMethod($name);
|
||||
$path = $is_method ? 'methods' : 'types';
|
||||
$empty = in_array($name, self::EMPTY_FIELDS);
|
||||
/* @var Dom $fields_table */
|
||||
$fields_table = $dom->find('table')[$i];
|
||||
$unparsed_fields = $fields_table->find('tbody')->find('tr');
|
||||
/* @var Dom\AbstractNode $element */
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
$data[$path][] = self::generateElement($name, $element->nextSibling()->nextSibling()->innerHtml,
|
||||
($empty ? null : $unparsed_fields), $is_method);
|
||||
if (!$empty) {
|
||||
$i++;
|
||||
$temp = $element;
|
||||
$description = '';
|
||||
$table = null;
|
||||
while (true) {
|
||||
try {
|
||||
$element = $element->nextSibling();
|
||||
} catch (ChildNotFoundException $e) {
|
||||
break;
|
||||
}
|
||||
$tag = $element->tag->name() ?? null;
|
||||
if (empty($temp->text()) or empty($tag) or $tag == 'text') {
|
||||
continue;
|
||||
} elseif (str_starts_with($tag, 'h')) {
|
||||
break;
|
||||
} elseif ($tag == 'p') {
|
||||
$description .= PHP_EOL . $element->innerHtml();
|
||||
} elseif ($tag == 'table') {
|
||||
$table = $element->find('tbody')->find('tr');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* @var Dom\AbstractNode $element */
|
||||
$data[$path][] = self::generateElement(
|
||||
$name,
|
||||
trim($description),
|
||||
$table,
|
||||
$is_method
|
||||
);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
@ -103,8 +142,7 @@ class Generator
|
||||
|
||||
private static function isMethod(string $name): bool
|
||||
{
|
||||
$first_letter = substr($name, 0, 1);
|
||||
return (strtolower($first_letter) == $first_letter);
|
||||
return lcfirst($name) == $name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,11 +151,11 @@ class Generator
|
||||
* @param Dom\Collection|null $unparsed_fields
|
||||
* @param bool $is_method
|
||||
* @return array
|
||||
* @throws \PHPHtmlParser\Exceptions\ChildNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\CircularException
|
||||
* @throws \PHPHtmlParser\Exceptions\CurlException
|
||||
* @throws \PHPHtmlParser\Exceptions\NotLoadedException
|
||||
* @throws \PHPHtmlParser\Exceptions\StrictException
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws CurlException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
*/
|
||||
private static function generateElement(
|
||||
string $name,
|
||||
@ -149,8 +187,8 @@ class Generator
|
||||
* @param Dom\Collection|null $fields
|
||||
* @param bool $is_method
|
||||
* @return array
|
||||
* @throws \PHPHtmlParser\Exceptions\ChildNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\NotLoadedException
|
||||
* @throws ChildNotFoundException
|
||||
* @throws NotLoadedException
|
||||
*/
|
||||
private static function parseFields(?Dom\Collection $fields, bool $is_method): array
|
||||
{
|
||||
@ -159,20 +197,27 @@ class Generator
|
||||
foreach ($fields as $field) {
|
||||
/* @var Dom $field */
|
||||
$field_data = $field->find('td');
|
||||
$name = $field_data[0]->text;
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
$parsed_data = [
|
||||
'name' => $field_data[0]->text,
|
||||
'name' => $name,
|
||||
'type' => strip_tags($field_data[1]->innerHtml)
|
||||
];
|
||||
$parsed_data['types'] = self::parseFieldTypes($parsed_data['type']);
|
||||
unset($parsed_data['type']);
|
||||
if ($is_method) {
|
||||
$parsed_data['required'] = ($field_data[2]->text == 'Yes');
|
||||
$parsed_data['types'] = self::parseMethodFieldTypes($parsed_data['type']);
|
||||
unset($parsed_data['type']);
|
||||
$parsed_data['description'] = htmlspecialchars_decode(strip_tags($field_data[3]->innerHtml ?? $field_data[3]->text ?? ''),
|
||||
ENT_QUOTES);
|
||||
$parsed_data['required'] = $field_data[2]->text == 'Yes';
|
||||
$parsed_data['description'] = htmlspecialchars_decode(
|
||||
strip_tags($field_data[3]->innerHtml ?? $field_data[3]->text ?? ''),
|
||||
ENT_QUOTES
|
||||
);
|
||||
} else {
|
||||
$parsed_data['type'] = self::parseObjectFieldType($parsed_data['type']);
|
||||
$parsed_data['description'] = htmlspecialchars_decode(strip_tags($field_data[2]->innerHtml),
|
||||
ENT_QUOTES);
|
||||
$parsed_data['description'] = htmlspecialchars_decode(
|
||||
strip_tags($field_data[2]->innerHtml),
|
||||
ENT_QUOTES
|
||||
);
|
||||
}
|
||||
$parsed_fields[] = $parsed_data;
|
||||
}
|
||||
@ -183,47 +228,57 @@ class Generator
|
||||
* @param string $raw_type
|
||||
* @return array
|
||||
*/
|
||||
private static function parseMethodFieldTypes(string $raw_type): array
|
||||
private static function parseFieldTypes(string $raw_type): array
|
||||
{
|
||||
$types = explode(' or ', $raw_type);
|
||||
$types = [];
|
||||
foreach (explode(' or ', $raw_type) as $raw_or_type) {
|
||||
if (stripos($raw_or_type, 'array') === 0) {
|
||||
$types[] = str_replace(' and', ',', $raw_or_type);
|
||||
continue;
|
||||
}
|
||||
foreach (explode(' and ', $raw_or_type) as $unparsed_type) {
|
||||
$types[] = $unparsed_type;
|
||||
}
|
||||
}
|
||||
$parsed_types = [];
|
||||
foreach ($types as $type) {
|
||||
$type = trim(str_replace(['number', 'of'], '', $type));
|
||||
$multiples_count = substr_count(strtolower($type), 'array');
|
||||
$parsed_types[] = trim(str_replace(['Array', 'Integer', 'String', 'Boolean', 'Float'],
|
||||
['', 'int', 'string', 'bool', 'float'], $type)) . str_repeat('[]', $multiples_count);
|
||||
$parsed_type = trim(
|
||||
str_replace(
|
||||
['Array', 'Integer', 'String', 'Boolean', 'Float', 'True'],
|
||||
['', 'int', 'string', 'bool', 'float', 'bool'],
|
||||
$type
|
||||
)
|
||||
);
|
||||
for ($i = 0; $i < $multiples_count; $i++) {
|
||||
$parsed_type = sprintf('Array<%s>', $parsed_type);
|
||||
}
|
||||
$parsed_types[] = $parsed_type;
|
||||
}
|
||||
return $parsed_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $raw_type
|
||||
* @return string
|
||||
*/
|
||||
private static function parseObjectFieldType(string $raw_type): string
|
||||
{
|
||||
$type = trim(str_replace(['number', 'of'], '', $raw_type));
|
||||
$multiples_count = substr_count(strtolower($type), 'array');
|
||||
return trim(str_replace(['Array', 'Integer', 'String', 'Boolean', 'Float'],
|
||||
['', 'int', 'string', 'bool', 'float'], $type)) . str_repeat('[]', $multiples_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
* @return array
|
||||
* @throws \PHPHtmlParser\Exceptions\ChildNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\CircularException
|
||||
* @throws \PHPHtmlParser\Exceptions\CurlException
|
||||
* @throws \PHPHtmlParser\Exceptions\NotLoadedException
|
||||
* @throws \PHPHtmlParser\Exceptions\StrictException
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws CurlException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
* @noinspection PhpUndefinedFieldInspection
|
||||
*/
|
||||
private static function parseReturnTypes(string $description): array
|
||||
{
|
||||
$return_types = [];
|
||||
$phrases = explode('.', $description);
|
||||
$phrases = array_filter($phrases, function ($phrase) {
|
||||
return (false !== stripos($phrase, 'returns') or false !== stripos($phrase, 'is returned'));
|
||||
});
|
||||
$phrases = array_filter(
|
||||
$phrases,
|
||||
function ($phrase) {
|
||||
return (false !== stripos($phrase, 'returns') or false !== stripos($phrase, 'is returned'));
|
||||
}
|
||||
);
|
||||
foreach ($phrases as $phrase) {
|
||||
$dom = new Dom;
|
||||
$dom->load($phrase);
|
||||
@ -231,11 +286,16 @@ class Generator
|
||||
$em = $dom->find('em');
|
||||
foreach ($a as $element) {
|
||||
if ($element->text == 'Messages') {
|
||||
$return_types[] = 'Message[]';
|
||||
$return_types[] = 'Array<Message>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$multiples_count = substr_count(strtolower($phrase), 'array');
|
||||
$return_types[] = $element->text . str_repeat('[]', $multiples_count);
|
||||
$return_type = $element->text;
|
||||
for ($i = 0; $i < $multiples_count; $i++) {
|
||||
$return_type = sprintf('Array<%s>', $return_type);
|
||||
}
|
||||
$return_types[] = $return_type;
|
||||
}
|
||||
foreach ($em as $element) {
|
||||
if (in_array($element->text, ['False', 'force', 'Array'])) {
|
||||
@ -251,16 +311,16 @@ class Generator
|
||||
/**
|
||||
* @param int $options
|
||||
* @return string
|
||||
* @throws \PHPHtmlParser\Exceptions\ChildNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\CircularException
|
||||
* @throws \PHPHtmlParser\Exceptions\CurlException
|
||||
* @throws \PHPHtmlParser\Exceptions\NotLoadedException
|
||||
* @throws \PHPHtmlParser\Exceptions\ParentNotFoundException
|
||||
* @throws \PHPHtmlParser\Exceptions\StrictException
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws CurlException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
*/
|
||||
public static function toJson(int $options = 0): string
|
||||
public function toJson(int $options = 0): string
|
||||
{
|
||||
$scheme = self::extractScheme();
|
||||
$scheme = $this->extractScheme();
|
||||
return json_encode($scheme, $options);
|
||||
}
|
||||
|
||||
|
213
src/StubCreator.php
Normal file
213
src/StubCreator.php
Normal file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/** @noinspection PhpInternalEntityUsedInspection */
|
||||
|
||||
|
||||
namespace TgScraper;
|
||||
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Nette\PhpGenerator\Helpers;
|
||||
use Nette\PhpGenerator\PhpFile;
|
||||
use Nette\PhpGenerator\PhpNamespace;
|
||||
use Nette\PhpGenerator\Type;
|
||||
|
||||
/**
|
||||
* Class StubCreator
|
||||
* @package TgScraper
|
||||
*/
|
||||
class StubCreator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $namespace;
|
||||
|
||||
/**
|
||||
* StubCreator constructor.
|
||||
* @param array $scheme
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function __construct(private array $scheme, string $namespace = '')
|
||||
{
|
||||
if (str_ends_with($namespace, '\\')) {
|
||||
$namespace = substr($namespace, 0, -1);
|
||||
}
|
||||
if (!empty($namespace)) {
|
||||
if (!Helpers::isNamespaceIdentifier($namespace)) {
|
||||
throw new InvalidArgumentException('Namespace invalid');
|
||||
}
|
||||
}
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fieldTypes
|
||||
* @return array
|
||||
*/
|
||||
private function parseFieldTypes(array $fieldTypes, PhpNamespace $phpNamespace): array
|
||||
{
|
||||
$types = [];
|
||||
$comments = [];
|
||||
foreach ($fieldTypes as $fieldType) {
|
||||
$comments[] = $fieldType;
|
||||
if (str_starts_with($fieldType, 'Array')) {
|
||||
$types[] = 'array';
|
||||
continue;
|
||||
}
|
||||
if (ucfirst($fieldType) == $fieldType) {
|
||||
$fieldType = $phpNamespace->getName() . '\\' . $fieldType;
|
||||
}
|
||||
$types[] = $fieldType;
|
||||
}
|
||||
$comments = empty($comments) ? '' : sprintf('@var %s', implode('|', $comments));
|
||||
return [
|
||||
'types' => implode('|', $types),
|
||||
'comments' => $comments
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $apiTypes
|
||||
* @param PhpNamespace $phpNamespace
|
||||
* @return array
|
||||
*/
|
||||
private function parseApiFieldTypes(array $apiTypes, PhpNamespace $phpNamespace): array
|
||||
{
|
||||
$types = [];
|
||||
$comments = [];
|
||||
foreach ($apiTypes as $apiType) {
|
||||
$comments[] = $apiType;
|
||||
if (str_starts_with($apiType, 'Array')) {
|
||||
$types[] = 'array';
|
||||
continue;
|
||||
}
|
||||
if (ucfirst($apiType) == $apiType) {
|
||||
$apiType = $this->namespace . '\\Types\\' . $apiType;
|
||||
$phpNamespace->addUse($apiType);
|
||||
}
|
||||
$types[] = $apiType;
|
||||
}
|
||||
$comments = empty($comments) ? '' : sprintf('@var %s', implode('|', $comments));
|
||||
return [
|
||||
'types' => implode('|', $types),
|
||||
'comments' => $comments
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return PhpFile[]
|
||||
*/
|
||||
private function generateDefaultTypes(string $namespace): array
|
||||
{
|
||||
$file = new PhpFile;
|
||||
$phpNamespace = $file->addNamespace($namespace);
|
||||
$response = $phpNamespace->addClass('Response')
|
||||
->setType('class');
|
||||
$response->addProperty('ok')
|
||||
->setPublic();
|
||||
$response->addProperty('result')
|
||||
->setPublic();
|
||||
$response->addProperty('error_code')
|
||||
->setPublic();
|
||||
$response->addProperty('description')
|
||||
->setPublic();
|
||||
return [
|
||||
'Response' => $file
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PhpFile[]
|
||||
*/
|
||||
private function generateTypes(): array
|
||||
{
|
||||
$namespace = $this->namespace . '\\Types';
|
||||
$types = $this->generateDefaultTypes($namespace);
|
||||
foreach ($this->scheme['types'] as $type) {
|
||||
$file = new PhpFile;
|
||||
$phpNamespace = $file->addNamespace($namespace);
|
||||
$typeClass = $phpNamespace->addClass($type['name'])
|
||||
->setType('class');
|
||||
foreach ($type['fields'] as $field) {
|
||||
['types' => $fieldTypes, 'comments' => $fieldComments] = $this->parseFieldTypes(
|
||||
$field['types'],
|
||||
$phpNamespace
|
||||
);
|
||||
$type_property = $typeClass->addProperty($field['name'])
|
||||
->setPublic()
|
||||
->setType($fieldTypes);
|
||||
if (!empty($fieldComments)) {
|
||||
$type_property->addComment($fieldComments);
|
||||
}
|
||||
}
|
||||
$types[$type['name']] = $file;
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function generateApi(): string
|
||||
{
|
||||
$file = new PhpFile;
|
||||
$file->addComment('@noinspection PhpUnused');
|
||||
$file->addComment('@noinspection PhpUnusedParameterInspection');
|
||||
$file->addComment('@noinspection PhpIncompatibleReturnTypeInspection');
|
||||
$file->addComment('@noinspection PhpVoidFunctionResultUsedInspection');
|
||||
$phpNamespace = $file->addNamespace($this->namespace);
|
||||
$apiClass = $phpNamespace->addClass('API')
|
||||
->setType('class');
|
||||
$apiClass->addMethod('__construct')
|
||||
->setPublic()
|
||||
->addPromotedParameter('client')
|
||||
->setType('\GuzzleHttp\Client')
|
||||
->setPrivate();
|
||||
$sendRequest = $apiClass->addMethod('sendRequest')
|
||||
->setPublic();
|
||||
$sendRequest->addParameter('method')
|
||||
->setType(Type::STRING);
|
||||
$sendRequest->addParameter('args')
|
||||
->setType(Type::ARRAY);
|
||||
$sendRequest->addBody('//TODO: add your logic here');
|
||||
foreach ($this->scheme['methods'] as $method) {
|
||||
$function = $apiClass->addMethod($method['name'])
|
||||
->setPublic()
|
||||
->addBody('$args = get_defined_vars();')
|
||||
->addBody('return $this->sendRequest(__FUNCTION__, $args);');
|
||||
$fields = $method['fields'];
|
||||
usort(
|
||||
$fields,
|
||||
function ($a, $b) {
|
||||
return $b['required'] - $a['required'];
|
||||
}
|
||||
);
|
||||
foreach ($fields as $field) {
|
||||
$types = $this->parseApiFieldTypes($field['types'], $phpNamespace)['types'];
|
||||
$parameter = $function->addParameter($field['name'])
|
||||
->setType($types);
|
||||
if (!$field['required']) {
|
||||
$parameter->setNullable()
|
||||
->setDefaultValue(null);
|
||||
}
|
||||
}
|
||||
$returnTypes = $this->parseApiFieldTypes($method['return_types'], $phpNamespace)['types'];
|
||||
$function->setReturnType($returnTypes);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function generateCode(): array
|
||||
{
|
||||
return [
|
||||
'types' => $this->generateTypes(),
|
||||
'api' => $this->generateApi()
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace TGBotApi;
|
||||
|
||||
use Nette\{PhpGenerator, PhpGenerator\Type};
|
||||
|
||||
class StubProvider
|
||||
{
|
||||
|
||||
private $namespace_prefix = '';
|
||||
private $methods = [];
|
||||
private $types = [];
|
||||
|
||||
/**
|
||||
* StubProvider constructor.
|
||||
* @param string $namespace_prefix
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(string $namespace_prefix = '')
|
||||
{
|
||||
if (substr($namespace_prefix, -1) == '\\') {
|
||||
$namespace_prefix = substr($namespace_prefix, 0, -1);
|
||||
}
|
||||
if (!empty($namespace_prefix)) {
|
||||
if (!PhpGenerator\Helpers::isNamespaceIdentifier($namespace_prefix)) {
|
||||
throw new \Exception('Invalid namespace prefix provided');
|
||||
}
|
||||
$namespace_prefix .= '\\';
|
||||
}
|
||||
$types_namespace = $namespace_prefix . 'Types';
|
||||
$this->namespace_prefix = $namespace_prefix;
|
||||
$this->methods = $this->createDefaultMethods();
|
||||
$this->types = $this->createDefaultTypes($types_namespace);
|
||||
}
|
||||
|
||||
private function createDefaultMethods(): array
|
||||
{
|
||||
$method_inteface = (new PhpGenerator\ClassType('MethodInterface'))
|
||||
->setInterface();
|
||||
$method_inteface->addMethod('getParams')
|
||||
->setPublic()
|
||||
->setReturnType(Type::ARRAY);
|
||||
$method_inteface->addMethod('getMethodName')
|
||||
->setPublic()
|
||||
->setStatic()
|
||||
->setReturnType(Type::STRING);
|
||||
$method_inteface->addMethod('isMultipart')
|
||||
->setPublic()
|
||||
->setReturnType(Type::BOOL);
|
||||
$method_inteface->addMethod('getResultParams')
|
||||
->setPublic()
|
||||
->setStatic()
|
||||
->setReturnType(Type::ARRAY);
|
||||
$method_abstract = (new PhpGenerator\ClassType('DefaultMethod'))
|
||||
->setClass()
|
||||
->setAbstract()
|
||||
->addImplement('MethodInterface');
|
||||
$method_abstract->addConstant('METHOD_NAME', '')
|
||||
->setPrivate();
|
||||
$method_abstract->addConstant('RESULT_TYPE', '')
|
||||
->setPrivate();
|
||||
$method_abstract->addConstant('MULTIPLE_RESULTS', false)
|
||||
->setPrivate();
|
||||
$method_abstract->addProperty('multipart', false)
|
||||
->setPrivate();
|
||||
$method_abstract->addMethod('getMethodName')
|
||||
->setPublic()
|
||||
->setStatic()
|
||||
->setReturnType(Type::STRING)
|
||||
->setBody('return static::METHOD_NAME;');
|
||||
$method_abstract->addMethod('isMultipart')
|
||||
->setPublic()
|
||||
->setReturnType(Type::BOOL)
|
||||
->setBody('return $this->multipart;');
|
||||
$method_abstract->addMethod('getResultParams')
|
||||
->setPublic()
|
||||
->setStatic()
|
||||
->setReturnType(Type::ARRAY)
|
||||
->addBody('return [')
|
||||
->addBody(' \'type\' => static::RESULT_TYPE,')
|
||||
->addBody(' \'multiple\' => static::MULTIPLE_RESULTS')
|
||||
->addBody('];');
|
||||
return [
|
||||
'MethodInterface' => $this->addNamespace($method_inteface, 'Methods'),
|
||||
'DefaultMethod' => $this->addNamespace($method_abstract, 'Methods')
|
||||
];
|
||||
}
|
||||
|
||||
private function addNamespace(string $code, string $sub_namespace): string
|
||||
{
|
||||
return '<?php' . str_repeat(PHP_EOL,
|
||||
2) . 'namespace ' . $this->namespace_prefix . $sub_namespace . ';' . str_repeat(PHP_EOL, 2) . $code;
|
||||
}
|
||||
|
||||
private function createDefaultTypes(string $namespace): array
|
||||
{
|
||||
$response = (new PhpGenerator\ClassType('Response'))
|
||||
->setType('class');
|
||||
$response->addProperty('ok')
|
||||
->setPublic();
|
||||
$response->addProperty('result')
|
||||
->setPublic();
|
||||
$response->addProperty('error_code')
|
||||
->setPublic();
|
||||
$response->addProperty('description')
|
||||
->setPublic();
|
||||
$response->addMethod('parseResponse')
|
||||
->setReturnType(Type::SELF)
|
||||
->setReturnNullable(true)
|
||||
->setPublic()
|
||||
->setStatic()
|
||||
->setBody('if (null == $response) {
|
||||
return null;
|
||||
}
|
||||
$parsed_response = (new self())
|
||||
->setOk($response->ok ?? null)
|
||||
->setErrorCode($response->error_code ?? null)
|
||||
->setDescription($response->description ?? null);
|
||||
if (empty($response->result)) {
|
||||
$parsed_response->setResult(null);
|
||||
} elseif (!empty($response->result->migrate_to_chat_id) or !empty($response->result->retry_after)) {
|
||||
$parsed_response->setResult(ResponseParameters::parseResponseParameters($response->result ?? null));
|
||||
} elseif (!empty($response->result_type)) {
|
||||
$result_class = sprintf(\'' . $namespace . '\\%s\', $response->result_type->class);
|
||||
$parsed_response->setResult(call_user_func([$result_class, $response->result_type->method],
|
||||
$response->result ?? null));
|
||||
} else {
|
||||
$parsed_response->setResult($response->result ?? null);
|
||||
}
|
||||
return $parsed_response;')
|
||||
->addParameter('response')
|
||||
->setType('\stdClass')
|
||||
->setNullable(true);
|
||||
$this->createSetters($response, [
|
||||
['name' => 'ok', 'type' => 'bool', 'nullable' => true],
|
||||
['name' => 'result', 'type' => null, 'nullable' => false],
|
||||
['name' => 'error_code', 'type' => 'int', 'nullable' => true],
|
||||
['name' => 'description', 'type' => 'string', 'nullable' => true]
|
||||
]);
|
||||
return ['Response' => $this->addNamespace($response, 'Types')];
|
||||
}
|
||||
|
||||
private function createSetters(PhpGenerator\ClassType $class, array $properties): void
|
||||
{
|
||||
foreach ($properties as $property) {
|
||||
$class->addMethod('set' . $this->getCamelCaseName($property['name']))
|
||||
->setPublic()
|
||||
->setReturnType(Type::SELF)
|
||||
->setBody('$this->' . $property['name'] . ' = $' . $property['name'] . ';' . PHP_EOL . 'return $this;')
|
||||
->addParameter($property['name'])
|
||||
->setType($property['type'])
|
||||
->setNullable($property['nullable']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private function getCamelCaseName(string $snake_case): string
|
||||
{
|
||||
return str_replace(' ', '', ucwords(str_replace('_', ' ', $snake_case)));
|
||||
}
|
||||
|
||||
public function generateCode(array $scheme): array
|
||||
{
|
||||
foreach ($scheme['types'] as $type) {
|
||||
$type_class = (new PhpGenerator\ClassType($type['name']))
|
||||
->setType('class');
|
||||
$fields = [];
|
||||
foreach ($type['fields'] as $field) {
|
||||
$type_class->addProperty($field['name'])
|
||||
->setPublic()
|
||||
->setType($field['type']);
|
||||
$fields[] = [
|
||||
'name' => $field['name'],
|
||||
'type' => $field['type'],
|
||||
'nullable' => true
|
||||
];
|
||||
}
|
||||
$this->createSetters($type_class, $fields);
|
||||
$this->types[$type['name']] = $this->addNamespace($type_class, 'Types');
|
||||
}
|
||||
foreach ($scheme['methods'] as $method) {
|
||||
$method_class = (new PhpGenerator\ClassType(ucfirst($method['name'])))
|
||||
->setType('class')
|
||||
->addExtend('DefaultMethod');
|
||||
$method_class->addConstant('METHOD_NAME', $method['name'])
|
||||
->setPrivate();
|
||||
$return_type = $method['return_types'][0];
|
||||
$multiple_results = false;
|
||||
if (substr($return_type, -2) == '[]') {
|
||||
$return_type = substr($return_type, 0, -2);
|
||||
$multiple_results = true;
|
||||
}
|
||||
$method_class->addConstant('RESULT_TYPE', $return_type)
|
||||
->setPrivate();
|
||||
$method_class->addConstant('MULTIPLE_RESULTS', $multiple_results)
|
||||
->setPrivate();
|
||||
$fields = [];
|
||||
$get_params = $method_class->addMethod('getParams')
|
||||
->setPublic()
|
||||
->setReturnType(Type::ARRAY)
|
||||
->addBody('return [');
|
||||
$last_index = array_key_last($method['fields']);
|
||||
foreach ($method['fields'] as $index => $field) {
|
||||
$field_type = count($field['types']) == 0 ? $field['types'][0] : null; //maybe there will be a better implementation
|
||||
$method_class->addProperty($field['name'])
|
||||
->setPublic()
|
||||
->setType($field_type);
|
||||
$comma = $index != $last_index ? ',' : '';
|
||||
$get_params->addBody(' \'' . $field['name'] . '\' => $this->' . $field['name'] . $comma);
|
||||
$fields[] = [
|
||||
'name' => $field['name'],
|
||||
'type' => $field_type,
|
||||
'nullable' => true
|
||||
];
|
||||
}
|
||||
$get_params->addBody('];');
|
||||
$this->createConstructor($method_class, $fields);
|
||||
$this->methods[ucfirst($method['name'])] = $this->addNamespace($method_class, 'Methods');
|
||||
}
|
||||
return [
|
||||
'methods' => $this->methods,
|
||||
'types' => $this->types
|
||||
];
|
||||
}
|
||||
|
||||
private function createConstructor(PhpGenerator\ClassType $class, array $properties): void
|
||||
{
|
||||
$method = $class->addMethod('__construct');
|
||||
foreach ($properties as $property) {
|
||||
$method->addBody('$this->' . $property['name'] . ' = $' . $property['name'] . ';')
|
||||
->addParameter($property['name'])
|
||||
->setType($property['type'])
|
||||
->setNullable($property['nullable']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user