Passed
Push — master ( 4bf53d...ce4836 )
by Sys
09:47
created

Generator::fromJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
namespace TgScraper;
4
5
use Exception;
6
use InvalidArgumentException;
7
use JetBrains\PhpStorm\ArrayShape;
8
use PHPHtmlParser\Exceptions\ChildNotFoundException;
9
use PHPHtmlParser\Exceptions\CircularException;
10
use PHPHtmlParser\Exceptions\ContentLengthException;
11
use PHPHtmlParser\Exceptions\LogicalException;
12
use PHPHtmlParser\Exceptions\NotLoadedException;
13
use PHPHtmlParser\Exceptions\ParentNotFoundException;
14
use PHPHtmlParser\Exceptions\StrictException;
15
use Psr\Http\Client\ClientExceptionInterface;
16
use Psr\Log\LoggerInterface;
17
use Symfony\Component\Yaml\Yaml;
18
use TgScraper\Common\SchemaExtractor;
19
use TgScraper\Common\StubCreator;
20
use TgScraper\Constants\Versions;
21
use Throwable;
22
23
/**
24
 * Class Generator
25
 * @package TgScraper
26
 */
27
class Generator
28
{
29
30
    /**
31
     * @var array
32
     */
33
    private array $schema;
34
35
    /**
36
     * Generator constructor.
37
     * @param LoggerInterface $logger
38
     * @param string $url
39
     * @param array|null $schema
40
     * @throws ChildNotFoundException
41
     * @throws CircularException
42
     * @throws ClientExceptionInterface
43
     * @throws ContentLengthException
44
     * @throws LogicalException
45
     * @throws NotLoadedException
46
     * @throws ParentNotFoundException
47
     * @throws StrictException
48
     * @throws Throwable
49
     */
50
    public function __construct(
51
        private LoggerInterface $logger,
52
        private string $url = Versions::LATEST,
53
        ?array $schema = null
54
    ) {
55
        if (empty($schema)) {
56
            $extractor = new SchemaExtractor($this->logger, $this->url);
57
            try {
58
                $this->logger->info('Schema not provided, extracting from URL.');
59
                $schema = $extractor->extract();
60
            } catch (Throwable $e) {
61
                $this->logger->critical(
62
                    'An exception occurred while trying to extract the schema: ' . $e->getMessage()
63
                );
64
                throw $e;
65
            }
66
        }
67
        /** @var array $schema */
68
        $this->schema = $schema;
69
    }
70
71
    /**
72
     * @throws ChildNotFoundException
73
     * @throws CircularException
74
     * @throws ParentNotFoundException
75
     * @throws StrictException
76
     * @throws ClientExceptionInterface
77
     * @throws NotLoadedException
78
     * @throws ContentLengthException
79
     * @throws LogicalException
80
     * @throws Throwable
81
     */
82
    public static function fromYaml(LoggerInterface $logger, string $yaml): self
83
    {
84
        $data = Yaml::parse($yaml);
85
        return new self($logger, schema: $data);
86
    }
87
88
    /**
89
     * @throws ChildNotFoundException
90
     * @throws ParentNotFoundException
91
     * @throws CircularException
92
     * @throws StrictException
93
     * @throws ClientExceptionInterface
94
     * @throws NotLoadedException
95
     * @throws ContentLengthException
96
     * @throws LogicalException
97
     * @throws Throwable
98
     */
99
    public static function fromJson(LoggerInterface $logger, string $json): self
100
    {
101
        $data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
102
        return new self($logger, schema: $data);
103
    }
104
105
    /**
106
     * @param string $directory
107
     * @param string $namespace
108
     * @return void
109
     * @throws Exception
110
     */
111
    public function toStubs(string $directory = '', string $namespace = 'TelegramApi'): void
112
    {
113
        try {
114
            $directory = self::getTargetDirectory($directory);
115
        } catch (Exception $e) {
116
            $this->logger->critical(
117
                'An exception occurred while trying to get the target directory: ' . $e->getMessage()
118
            );
119
            throw $e;
120
        }
121
        try {
122
            $creator = new StubCreator($this->schema, $namespace);
123
        } catch (InvalidArgumentException $e) {
124
            $this->logger->critical(
125
                'An exception occurred while trying to parse the schema: ' . $e->getMessage()
126
            );
127
            throw $e;
128
        }
129
        $code = $creator->generateCode();
130
        foreach ($code['types'] as $className => $type) {
131
            $this->logger->info('Generating class for Type: ' . $className);
132
            $filename = sprintf('%s/Types/%s.php', $directory, $className);
133
            file_put_contents($filename, $type);
134
        }
135
        file_put_contents($directory . '/API.php', $code['api']);
136
    }
137
138
    /**
139
     * @param string $path
140
     * @return string
141
     * @throws Exception
142
     */
143
    private static function getTargetDirectory(string $path): string
144
    {
145
        $result = realpath($path);
146
        if (false === $result) {
147
            if (!mkdir($path)) {
148
                $path = __DIR__ . '/../generated';
149
                if (!file_exists($path)) {
150
                    mkdir($path, 0755);
151
                }
152
            }
153
        }
154
        $result = realpath($path);
155
        if (false === $result) {
156
            throw new Exception('Could not create target directory');
157
        }
158
        @mkdir($result . '/Types', 0755);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

158
        /** @scrutinizer ignore-unhandled */ @mkdir($result . '/Types', 0755);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
159
        return $result;
160
    }
161
162
    /**
163
     * @param int $options
164
     * @return string
165
     */
166
    public function toJson(int $options = 0): string
167
    {
168
        return json_encode($this->schema, $options);
169
    }
170
171
    /**
172
     * @param int $inline
173
     * @param int $indent
174
     * @param int $flags
175
     * @return string
176
     */
177
    public function toYaml(int $inline = 6, int $indent = 4, int $flags = 0): string
178
    {
179
        return Yaml::dump($this->schema, $inline, $indent, $flags);
180
    }
181
182
183
    /**
184
     * Thanks to davtur19 (https://github.com/davtur19/TuriBotGen/blob/master/postman.php)
185
     * @param int $options
186
     * @return string
187
     */
188
    #[ArrayShape(['info' => "string[]", 'variable' => "string[]", 'item' => "array[]"])]
189
    public function toPostman(
190
        int $options = 0
191
    ): string {
192
        $result = [
193
            'info' => [
194
                'name' => 'Telegram Bot API',
195
                'schema' => 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
196
            ],
197
            'variable' => [
198
                'key' => 'token',
199
                'value' => '1234:AAbbcc',
200
                'type' => 'string'
201
            ]
202
        ];
203
        foreach ($this->schema['methods'] as $method) {
204
            $formData = [];
205
            if (!empty($method['fields'])) {
206
                foreach ($method['fields'] as $field) {
207
                    $formData[] = [
208
                        'key' => $field['name'],
209
                        'value' => '',
210
                        'description' => sprintf(
211
                            '%s. %s',
212
                            $field['required'] ? 'Required' : 'Optional',
213
                            $field['description']
214
                        ),
215
                        'type' => 'text'
216
                    ];
217
                }
218
            }
219
            $result['item'][] = [
220
                'name' => $method['name'],
221
                'request' => [
222
                    'method' => 'POST',
223
                    'body' => [
224
                        'mode' => 'formdata',
225
                        'formdata' => $formData
226
                    ],
227
                    'url' => [
228
                        'raw' => 'https://api.telegram.org/bot{{token}}/' . $method['name'],
229
                        'protocol' => 'https',
230
                        'host' => [
231
                            'api',
232
                            'telegram',
233
                            'org'
234
                        ],
235
                        'path' => [
236
                            'bot{{token}}',
237
                            $method['name']
238
                        ]
239
                    ],
240
                    'description' => $method['description']
241
                ]
242
            ];
243
        }
244
        return json_encode($result, $options);
245
    }
246
247
}
248