self::MAX_INPUT) { throw new \LengthException('Лимит Markdown — 8192 байта UTF-8.'); } $config = [ 'html_input' => $htmlPolicy, 'allow_unsafe_links' => false, 'max_nesting_level' => 20, 'max_delimiters_per_line' => 200, 'heading_permalink' => ['id_prefix' => 'doc', 'fragment_prefix' => 'doc', 'apply_id_to_heading' => true, 'title' => 'Ссылка на раздел', 'symbol' => '#'], 'table_of_contents' => ['position' => 'placeholder', 'placeholder' => '[TOC]', 'min_heading_level' => 2, 'max_heading_level' => 3, 'max_placeholder_entries' => 100], 'external_link' => ['internal_hosts' => ['ae-nekrasov.ru'], 'open_in_new_window' => true, 'noopener' => 'external', 'noreferrer' => 'external', 'nofollow' => 'external'], ]; $environment = new Environment($config); $environment->addExtension(new CommonMarkCoreExtension()); if (in_array($profile, ['gfm', 'docs', 'links', 'notes'], true)) { $environment->addExtension(new GithubFlavoredMarkdownExtension()); } if ($profile === 'docs') { $environment->addExtension(new HeadingPermalinkExtension()); $environment->addExtension(new TableOfContentsExtension()); } if ($profile === 'links') { $environment->addExtension(new ExternalLinkExtension()); } if ($profile === 'notes') { $environment->addExtension(new NoteExtension()); } $started = hrtime(true); $html = (string) (new MarkdownConverter($environment))->convert($markdown); if (strlen($html) > self::MAX_OUTPUT) { throw new \LengthException('HTML превышает лимит 128 КиБ.'); } return ['html' => $html, 'inputBytes' => strlen($markdown), 'outputBytes' => strlen($html), 'elapsedMs' => round((hrtime(true) - $started) / 1e6, 3), 'profile' => $profile, 'htmlPolicy' => $htmlPolicy, 'engine' => 'league/commonmark 2.10.7']; } public function request(array $data): array { foreach (array_keys($data) as $key) { if (!in_array($key, ['markdown', 'documents', 'profile', 'htmlPolicy'], true)) { throw new \InvalidArgumentException('Неизвестное поле запроса.'); } } $profile = array_key_exists('profile', $data) ? $data['profile'] : 'core'; $policy = array_key_exists('htmlPolicy', $data) ? $data['htmlPolicy'] : 'escape'; if (!is_string($profile) || !is_string($policy)) { throw new \InvalidArgumentException('Профиль и политика должны быть строками.'); } if (array_key_exists('documents', $data)) { if (array_key_exists('markdown', $data) || !is_array($data['documents']) || !array_is_list($data['documents']) || count($data['documents']) < 1 || count($data['documents']) > 5) { throw new \InvalidArgumentException('Пакет: от 1 до 5 строк Markdown без поля markdown.'); } $bytes = 0; foreach ($data['documents'] as $document) { if (!is_string($document)) { throw new \InvalidArgumentException('Каждый документ должен быть строкой.'); } $bytes += strlen($document); } if ($bytes > self::MAX_INPUT) { throw new \LengthException('Суммарный лимит пакета — 8192 байта.'); } $results = array_map(fn(string $m): array => $this->convert($m, $profile, $policy), $data['documents']); if (array_sum(array_column($results, 'outputBytes')) > self::MAX_OUTPUT) { throw new \LengthException('Пакет HTML превышает лимит 128 КиБ.'); } return ['ok' => true, 'mode' => 'batch', 'results' => $results]; } if (!isset($data['markdown']) || !is_string($data['markdown'])) { throw new \InvalidArgumentException('Поле markdown должно быть строкой.'); } return ['ok' => true, 'mode' => 'single', 'results' => [$this->convert($data['markdown'], $profile, $policy)]]; } }