作者 lackoxygen

add all

  1 +/vendor
  2 +/.idea
  3 +/.vscode
  4 +.env
  5 +.phpunit.result.cache
  6 +composer.lock
  1 +{
  2 + "name": "lackoxygen/showdoc-generation",
  3 + "type": "library",
  4 + "autoload": {
  5 + "psr-4": {
  6 + "Lackoxygen\\ShowDocGeneration\\": "src/"
  7 + }
  8 + },
  9 + "authors": [
  10 + {
  11 + "name": "lackoxygen",
  12 + "email": "jingze666@gmail.com"
  13 + }
  14 + ],
  15 + "extra": {
  16 + "laravel": {
  17 + "providers": [
  18 + "Lackoxygen\\ShowDocGeneration\\ShowDocGenerationServiceProvider"
  19 + ]
  20 + }
  21 + },
  22 + "require": {
  23 + "doctrine/annotations": "1.14.3"
  24 + }
  25 +}
  1 +<?php
  2 +
  3 +return [
  4 + 'api_domain' => '',
  5 + 'api_key' => '',
  6 + 'api_token' => ''
  7 +];
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +abstract class Annotation
  6 +{
  7 + public function __construct(array $values)
  8 + {
  9 + foreach ($this->getSelfProperties() as $property) {
  10 + if (isset($values[$property->getName()])) {
  11 + $this->{$property->getName()} = $values[$property->getName()];
  12 + }
  13 + }
  14 + }
  15 +
  16 + /**
  17 + * @return mixed|\ReflectionProperty[]
  18 + */
  19 + protected function getSelfProperties()
  20 + {
  21 + static $properties;
  22 +
  23 + if (is_null($properties)) {
  24 + $ref = new \ReflectionClass($this);
  25 +
  26 + $properties = $ref->getProperties();
  27 + }
  28 +
  29 + return $properties;
  30 + }
  31 +
  32 + /**
  33 + * @return array
  34 + */
  35 + public function toArray(): array
  36 + {
  37 + $array = [];
  38 + foreach ($this->getSelfProperties() as $property) {
  39 + $array[$property->getName()] = $this->{$property->getName()};
  40 + }
  41 + return $array;
  42 + }
  43 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({})
  11 + */
  12 +final class Catalog extends Annotation
  13 +{
  14 + /**
  15 + * @Required()
  16 + * @var string
  17 + */
  18 + protected string $value = '';
  19 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +/**
  6 + * @Annotation
  7 + * @Target({"METHOD"})
  8 + * @Attributes({
  9 +@Attribute("value", type = "string"),
  10 + * })
  11 + */
  12 +final class Description extends Annotation
  13 +{
  14 + /**
  15 + * @Required()
  16 + * @var string
  17 + */
  18 + protected string $value = '';
  19 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({
  11 +@Attribute("key", type = "string"),
  12 +@Attribute("remark", type = "string"),
  13 + * })
  14 + */
  15 +final class HeaderLine extends Annotation
  16 +{
  17 + /**
  18 + * @Required()
  19 + * @var string
  20 + */
  21 + protected string $key = '';
  22 +
  23 + /**
  24 + * @Required()
  25 + * @var string
  26 + */
  27 + protected string $remark = '';
  28 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Enum;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({
  11 +@Attribute("method", type = "string"),
  12 + * })
  13 + */
  14 +final class Method extends Annotation
  15 +{
  16 + public const METHOD_GET = 'GET';
  17 + public const METHOD_POST = 'POST';
  18 + public const METHOD_PUT = 'PUT';
  19 + public const METHOD_PATCH = 'PATCH';
  20 + public const METHOD_DELETE = 'DELETE';
  21 +
  22 + /**
  23 + * @Required()
  24 + * @Enum({"GET", "POST", "PUT", "PATCH", "DELETE"})
  25 + * @var string
  26 + */
  27 + protected string $method = '';
  28 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({
  11 +@Attribute("key", type = "string"),
  12 +@Attribute("must", type = "bool"),
  13 +@Attribute("type", type = "string"),
  14 +@Attribute("remark", type = "string"),
  15 + * })
  16 + */
  17 +final class ParamLine extends Annotation
  18 +{
  19 + /**
  20 + * @Required()
  21 + * @var string
  22 + */
  23 + protected string $key = '';
  24 + /**
  25 + * @var bool
  26 + */
  27 + protected bool $must = false;
  28 +
  29 + /**
  30 + * @Required()
  31 + * @var string
  32 + */
  33 + protected string $type = '';
  34 +
  35 + /**
  36 + * @Required()
  37 + * @var string
  38 + */
  39 + protected string $remark = '';
  40 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({})
  11 + */
  12 +final class Remark extends Annotation
  13 +{
  14 + /**
  15 + * @Required()
  16 + * @var string
  17 + */
  18 + protected string $value = '';
  19 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({
  11 +@Attribute("value", type = "string"),
  12 + * })
  13 + */
  14 +final class Resp extends Annotation
  15 +{
  16 + /**
  17 + * @Required()
  18 + * @var string
  19 + */
  20 + protected string $value = '{}';
  21 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +use Doctrine\Common\Annotations\Annotation\Required;
  6 +
  7 +/**
  8 + * @Annotation
  9 + * @Target({"METHOD"})
  10 + * @Attributes({
  11 +@Attribute("key", type = "string"),
  12 +@Attribute("type", type = "string"),
  13 +@Attribute("remark", type = "string"),
  14 + * })
  15 + */
  16 +class RespLine extends Annotation
  17 +{
  18 + /**
  19 + * @Required()
  20 + * @var string
  21 + */
  22 + protected string $key = '';
  23 +
  24 + /**
  25 + * @Required()
  26 + * @var string
  27 + */
  28 + protected string $type = '';
  29 +
  30 + /**
  31 + * @Required()
  32 + * @var string
  33 + */
  34 + protected string $remark = '';
  35 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +/**
  6 + * @Annotation
  7 + * @Target({"METHOD"})
  8 + * @Attributes({
  9 +@Attribute("value", type = "string"),
  10 + * })
  11 + */
  12 +final class Title extends Annotation
  13 +{
  14 + /**
  15 + * @Required()
  16 + * @var string
  17 + */
  18 + protected string $value = '';
  19 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Annotations;
  4 +
  5 +/**
  6 + * @Annotation
  7 + * @Target({"METHOD"})
  8 + * @Attributes({
  9 +@Attribute("path", type = "string"),
  10 + * })
  11 + */
  12 +final class Url extends Annotation
  13 +{
  14 + /**
  15 + * @Required()
  16 + * @var string
  17 + */
  18 + protected string $path = '';
  19 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Commands;
  4 +
  5 +use Illuminate\Console\Command;
  6 +use Lackoxygen\ShowDocGeneration\Logger;
  7 +use Lackoxygen\ShowDocGeneration\Parser\Paster;
  8 +use Lackoxygen\ShowDocGeneration\Writer\Writer;
  9 +
  10 +class GenerationCommand extends Command
  11 +{
  12 + /**
  13 + * The name and signature of the console command.
  14 + *
  15 + * @var string
  16 + */
  17 + protected $signature = 'doc {--v : print info}
  18 + {--prefix=}';
  19 +
  20 + /**
  21 + * The console command description.
  22 + *
  23 + * @var string
  24 + */
  25 + protected $description = 'generate documentation';
  26 +
  27 + /**
  28 + * Create a new command instance.
  29 + *
  30 + * @return void
  31 + */
  32 + public function __construct()
  33 + {
  34 + parent::__construct();
  35 + }
  36 +
  37 + /**
  38 + * Execute the console command.
  39 + *
  40 + * @return int
  41 + */
  42 + public function handle()
  43 + {
  44 +
  45 + $v = $this->input->getOption('v');
  46 +
  47 + Logger::setMode($v ? Logger::DISPLAY : Logger::NONE);
  48 +
  49 + Logger::setOutput($this->output);
  50 +
  51 + Logger::writeln("input option v={$v}");
  52 +
  53 + $prefix = $this->input->getOption('prefix') ?? 'api';
  54 +
  55 + Logger::writeln("input option prefix={$prefix}");
  56 +
  57 + Logger::writeln('parse start');
  58 + $cos = Paster::resolve($this->laravel, $prefix);
  59 +
  60 + Logger::writeln('parse end');
  61 +
  62 + Logger::writeln('write start');
  63 + $writer = new Writer();
  64 +
  65 + $writer->puts($cos);
  66 +
  67 + Logger::writeln('write end');
  68 +
  69 + return 0;
  70 + }
  71 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration;
  4 +
  5 +use Symfony\Component\Console\Output\ConsoleOutput;
  6 +use Symfony\Component\Console\Output\OutputInterface;
  7 +
  8 +class Logger
  9 +{
  10 + public const NONE = 0;
  11 +
  12 + public const DISPLAY = 1;
  13 +
  14 + /**
  15 + * @var int
  16 + */
  17 + private static int $mode;
  18 +
  19 + /**
  20 + * @var OutputInterface $outputInterface
  21 + */
  22 + private static OutputInterface $outputInterface;
  23 +
  24 + /**
  25 + * @return void
  26 + */
  27 + public static function setMode(int $mode)
  28 + {
  29 + self::$mode = $mode;
  30 + }
  31 +
  32 + public static function setOutput(OutputInterface $output)
  33 + {
  34 + self::$outputInterface = $output;
  35 + }
  36 +
  37 + public static function writeln(string $message)
  38 + {
  39 + if (self::$mode) {
  40 + self::$outputInterface->writeln(sprintf("[%s]: %s", now()->toString(), $message));
  41 + }
  42 + }
  43 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Parser;
  4 +
  5 +class Doc
  6 +{
  7 + public array $catalog = [];
  8 + public array $title = [];
  9 +
  10 + public array $method = [];
  11 +
  12 + public array $url = [];
  13 +
  14 + public array $headerLine = [];
  15 +
  16 + public array $paramLine = [];
  17 +
  18 + public array $resp = [];
  19 +
  20 + public array $respLine = [];
  21 +
  22 + public array $remark = [];
  23 +
  24 + public array $middles = [];
  25 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Parser;
  4 +
  5 +use Illuminate\Foundation\Application;
  6 +use Illuminate\Routing\Route;
  7 +use Illuminate\Routing\RouteCollection;
  8 +use Illuminate\Routing\Router;
  9 +use Illuminate\Support\Collection;
  10 +use Illuminate\Support\Str;
  11 +use Lackoxygen\ShowDocGeneration\Annotations\Annotation;
  12 +use Lackoxygen\ShowDocGeneration\Annotations\Catalog;
  13 +use Lackoxygen\ShowDocGeneration\Annotations\HeaderLine;
  14 +use Lackoxygen\ShowDocGeneration\Annotations\Method;
  15 +use Lackoxygen\ShowDocGeneration\Annotations\ParamLine;
  16 +use Lackoxygen\ShowDocGeneration\Annotations\Remark;
  17 +use Lackoxygen\ShowDocGeneration\Annotations\Resp;
  18 +use Lackoxygen\ShowDocGeneration\Annotations\RespLine;
  19 +use Lackoxygen\ShowDocGeneration\Annotations\Title;
  20 +use Lackoxygen\ShowDocGeneration\Annotations\Url;
  21 +use Lackoxygen\ShowDocGeneration\Logger;
  22 +use Psr\Container\ContainerExceptionInterface;
  23 +use Psr\Container\NotFoundExceptionInterface;
  24 +
  25 +class Paster
  26 +{
  27 + /**
  28 + * @var array
  29 + */
  30 + protected array $annotations = [
  31 + Catalog::class,
  32 + Title::class,
  33 + Url::class,
  34 + Method::class,
  35 + HeaderLine::class,
  36 + ParamLine::class,
  37 + Resp::class,
  38 + RespLine::class,
  39 + Remark::class
  40 + ];
  41 +
  42 + /**
  43 + * @var Application
  44 + */
  45 + protected Application $application;
  46 +
  47 + /**
  48 + * @var string
  49 + */
  50 + protected string $prefix;
  51 +
  52 + /**
  53 + * @var Collection|array
  54 + */
  55 + protected Collection $docsCollect;
  56 +
  57 + public function __construct()
  58 + {
  59 + $this->docsCollect = collect();
  60 + }
  61 +
  62 + /**
  63 + * @param Application $application
  64 + * @param string|null $prefix
  65 + * @return array|Collection
  66 + */
  67 + public static function resolve(Application $application, string $prefix = null)
  68 + {
  69 + $static = new static();
  70 + $static->application = $application;
  71 + $static->prefix = $prefix;
  72 + try {
  73 + $routes = $static->getRoutes();
  74 +
  75 + $static->eachRoutes($routes, function (self $self, Route $route) {
  76 + Logger::writeln('load ' . join('|', $route->methods()) . ' ' . $route->uri());
  77 + try {
  78 + $doc = $self->resolveAnnotation($route);
  79 + $doc->middles = $route->middleware();
  80 + $self->docsCollect->push($doc);
  81 + } catch (\ReflectionException $e) {
  82 + Logger::writeln('error ' . $e->getMessage());
  83 + }
  84 + });
  85 + } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
  86 + Logger::writeln('error ' . $e->getMessage());
  87 + }
  88 +
  89 + return $static->docsCollect;
  90 + }
  91 +
  92 + /**
  93 + * @param RouteCollection $routes
  94 + * @param $callback
  95 + * @return void
  96 + */
  97 + protected function eachRoutes(RouteCollection $routes, $callback)
  98 + {
  99 + /**
  100 + * @var Route $route
  101 + */
  102 + foreach ($routes as $route) {
  103 + if ($this->isMatch($route)) {
  104 + $callback($this, $route);
  105 + }
  106 + }
  107 + }
  108 +
  109 + /**
  110 + * @param Route $route
  111 + * @return bool
  112 + */
  113 + protected function isMatch(Route $route): bool
  114 + {
  115 + if ($this->prefix && $prefixArray = explode(',', $this->prefix)) {
  116 + if (!in_array($route->getPrefix(), $prefixArray)) {
  117 + return false;
  118 + }
  119 + }
  120 +
  121 + $action = $route->getAction();
  122 +
  123 + if (!isset($action['controller'])) {
  124 + return false;
  125 + }
  126 +
  127 + return true;
  128 + }
  129 +
  130 + /**
  131 + * @param Route $route
  132 + * @return Doc
  133 + * @throws \ReflectionException
  134 + */
  135 + protected function resolveAnnotation(Route $route): Doc
  136 + {
  137 + Logger::writeln('parse ' . join('|', $route->methods()) . ' ' . $route->uri() . ' Annotation');
  138 + $reader = new \Doctrine\Common\Annotations\AnnotationReader();
  139 +
  140 + $refClass = new \ReflectionClass(($route->getController()));
  141 + $method = $refClass->getMethod($route->getActionMethod());
  142 +
  143 + $doc = new Doc();
  144 +
  145 + foreach ($this->annotations as $annot) {
  146 +
  147 + Logger::writeln('parse ' . join('|', $route->methods()) . ' ' . $route->uri() . ' method annotation ' . $annot);
  148 +
  149 + $annotation = $reader->getMethodAnnotation($method, $annot);
  150 +
  151 + $pro = Str::camel(class_basename($annot));
  152 +
  153 + if (!$annotation instanceof Annotation) {
  154 + continue;
  155 + }
  156 + if (Str::endsWith($annot, 'Line')) {
  157 + $doc->{$pro}[] = $annotation->toArray();
  158 + } else {
  159 + $doc->{$pro} = $annotation->toArray();
  160 + }
  161 + }
  162 +
  163 + return $doc;
  164 + }
  165 +
  166 + /**
  167 + * @return \Illuminate\Routing\RouteCollectionInterface|RouteCollection
  168 + * @throws ContainerExceptionInterface
  169 + * @throws NotFoundExceptionInterface
  170 + */
  171 + protected function getRoutes()
  172 + {
  173 + /**
  174 + * @var Router $router
  175 + */
  176 + $router = $this->application->get('router');
  177 +
  178 + return $router->getRoutes();
  179 + }
  180 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration;
  4 +
  5 +use Illuminate\Support\ServiceProvider;
  6 +use Lackoxygen\ShowDocGeneration\Commands\GenerationCommand;
  7 +
  8 +class ShowDocGenerationServiceProvider extends ServiceProvider
  9 +{
  10 + public function boot()
  11 + {
  12 + if ($this->app->runningInConsole()) {
  13 + $configPath = __DIR__ . '/../publish/doc.php';
  14 + $this->publishes([
  15 + $configPath => config_path('doc.php')
  16 + ], 'lackoxygen-doc');
  17 + }
  18 + }
  19 +
  20 + public function register()
  21 + {
  22 + $this->commands([
  23 + GenerationCommand::class
  24 + ]);
  25 + }
  26 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Writer;
  4 +
  5 +use GuzzleHttp\Exception\GuzzleException;
  6 +use GuzzleHttp\RequestOptions;
  7 +use Illuminate\Support\Arr;
  8 +use Lackoxygen\ShowDocGeneration\Logger;
  9 +use Lackoxygen\ShowDocGeneration\Parser\Doc;
  10 +
  11 +class Client
  12 +{
  13 + protected \GuzzleHttp\Client $engine;
  14 +
  15 + /**
  16 + * @var array
  17 + */
  18 + protected array $config;
  19 +
  20 + public function __construct()
  21 + {
  22 + Logger::writeln('new GuzzleHttp\\Client');
  23 + $this->engine = new \GuzzleHttp\Client([
  24 + RequestOptions::TIMEOUT => 5.00
  25 + ]);
  26 +
  27 + $this->config = (array)config('doc', []);
  28 +
  29 + Logger::writeln('read doc config ' . \json_encode($this->config));
  30 + }
  31 +
  32 + /**
  33 + * @param Doc $doc
  34 + * @return void
  35 + */
  36 + public function send(Doc $doc)
  37 + {
  38 + try {
  39 + $params = [
  40 + 'api_key' => (string)Arr::get($this->config, 'api_key'),
  41 + 'api_token' => (string)Arr::get($this->config, 'api_token'),
  42 + 'cat_name' => Arr::get($doc->catalog, 'value', ''),
  43 + 'page_title' => Arr::get($doc->title, 'value', ''),
  44 + 'page_content' => (new Markdown($doc))->toString(),
  45 + 's_number' => 99
  46 + ];
  47 + Logger::writeln('guzzle form params ' . json_encode($params, JSON_UNESCAPED_UNICODE));
  48 + $ret = $this->engine->post((string)Arr::get($this->config, 'api_url'),
  49 + [
  50 + RequestOptions::FORM_PARAMS => $params
  51 + ]
  52 + );
  53 + Logger::writeln('guzzle status ' . $ret->getStatusCode());
  54 + Logger::writeln('guzzle response ' . \json_encode(\json_decode($ret->getBody()->getContents()), JSON_UNESCAPED_UNICODE));
  55 + } catch (GuzzleException $e) {
  56 + Logger::writeln('guzzle error .' . $e->getMessage());
  57 + }
  58 + }
  59 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Writer;
  4 +
  5 +use Illuminate\Support\Arr;
  6 +use Lackoxygen\ShowDocGeneration\Annotations\Method;
  7 +use Lackoxygen\ShowDocGeneration\Parser\Doc;
  8 +
  9 +class Markdown
  10 +{
  11 + protected string $template = <<<t
  12 +##### 简要描述
  13 +
  14 +- {{title}}
  15 +
  16 +##### 请求URL
  17 +- ` {{url}} `
  18 +
  19 +##### 请求方式
  20 +- {{method}}
  21 +
  22 +##### 参数
  23 +|参数名|必选|类型|说明|
  24 +|:----|:---|:----- |-----|
  25 +{{params}}
  26 +
  27 +##### 中间件
  28 +{{middles}}
  29 +
  30 +##### 请求头
  31 +|参数名|说明|
  32 +|:---- |:---|
  33 +{{headers}}
  34 +
  35 +##### 返回示例
  36 +
  37 +```
  38 +{{resp}}
  39 +```
  40 +
  41 +##### 返回参数说明
  42 +
  43 +|参数名|类型|说明|
  44 +|:-----|:-----|:-----------------------------|
  45 +{{respRemark}}
  46 +
  47 +##### 备注
  48 +
  49 +- 最后更新时间:{{updateTime}}
  50 +t;
  51 +
  52 + protected string $output = '';
  53 +
  54 + public function __construct(Doc $doc)
  55 + {
  56 + $editTemplate = $this->template;
  57 +
  58 + $replaces = [
  59 + 'title' => Arr::get($doc->title, 'value'),
  60 + 'url' => Arr::get($doc->url, 'path'),
  61 + 'method' => Arr::get($doc->method, 'method', Method::METHOD_GET),
  62 + 'headers' => join("\n", $this->tableTbody($doc->headerLine)),
  63 + 'params' => join("\n", $this->tableTbody($doc->paramLine)),
  64 + 'resp' => Arr::get($doc->resp, 'value', '{}'),
  65 + 'respRemark' => join("\n", $this->tableTbody($doc->respLine)),
  66 + 'middles' => join("\n", $this->unorderedList($doc->middles)),
  67 + 'updateTime' => now()->toString()
  68 + ];
  69 +
  70 + $this->output = \str_replace(
  71 + \array_map(function (string $key) {
  72 + return '{{' . $key . '}}';
  73 + }, \array_keys($replaces)),
  74 + \array_values($replaces),
  75 + $editTemplate
  76 + );
  77 + }
  78 +
  79 + protected function unorderedList(array $list): array
  80 + {
  81 + return \array_map(function ($v) {
  82 + return '- ' . $v;
  83 + }, $list);
  84 + }
  85 +
  86 + protected function tableTbody(array $tbody): array
  87 + {
  88 + return \array_map(function (array $line) {
  89 + $line = \array_map(function ($v) {
  90 + if (\is_bool($v)) {
  91 + $v = $v ? '是' : '否';
  92 + }
  93 + return $v;
  94 + }, $line);
  95 + return '|' . \join('|', \array_values($line)) . '|';
  96 + }, $tbody);
  97 + }
  98 +
  99 + public function toString(): string
  100 + {
  101 + return $this->output;
  102 + }
  103 +}
  1 +<?php
  2 +
  3 +namespace Lackoxygen\ShowDocGeneration\Writer;
  4 +
  5 +use Illuminate\Support\Collection;
  6 +use Lackoxygen\ShowDocGeneration\Parser\Doc;
  7 +
  8 +class Writer
  9 +{
  10 + public function puts(Collection $docs)
  11 + {
  12 + $client = new Client();
  13 +
  14 + $docs->each(function (Doc $doc) use ($client) {
  15 + $client->send($doc);
  16 + });
  17 + }
  18 +}