Paster.php 4.9 KB
<?php

namespace Lackoxygen\ShowDocGeneration\Parser;

use Illuminate\Foundation\Application;
use Illuminate\Routing\Route;
use Illuminate\Routing\RouteCollection;
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Lackoxygen\ShowDocGeneration\Annotations\Annotation;
use Lackoxygen\ShowDocGeneration\Annotations\Catalog;
use Lackoxygen\ShowDocGeneration\Annotations\HeaderLine;
use Lackoxygen\ShowDocGeneration\Annotations\Method;
use Lackoxygen\ShowDocGeneration\Annotations\ParamLine;
use Lackoxygen\ShowDocGeneration\Annotations\Remark;
use Lackoxygen\ShowDocGeneration\Annotations\RespLine;
use Lackoxygen\ShowDocGeneration\Annotations\RespResource;
use Lackoxygen\ShowDocGeneration\Annotations\Title;
use Lackoxygen\ShowDocGeneration\Annotations\Url;
use Lackoxygen\ShowDocGeneration\Logger;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class Paster
{
    /**
     * @var array
     */
    protected array $annotations = [
        Catalog::class,
        Title::class,
        Url::class,
        Method::class,
        HeaderLine::class,
        ParamLine::class,
        RespLine::class,
        Remark::class,
        RespResource::class
    ];

    /**
     * @var Application
     */
    protected Application $application;

    /**
     * @var string
     */
    protected string $prefix;

    /**
     * @var Collection|array
     */
    protected Collection $docsCollect;

    public function __construct()
    {
        $this->docsCollect = collect();
    }

    /**
     * @param Application $application
     * @param string|null $prefix
     * @return array|Collection
     */
    public static function resolve(Application $application, string $prefix = null)
    {
        $static = new static();
        $static->application = $application;
        $static->prefix = $prefix;
        try {
            $routes = $static->getRoutes();

            $static->eachRoutes($routes, function (self $self, Route $route) {
                Logger::writeln('load ' . join('|', $route->methods()) . ' ' . $route->uri());
                try {
                    $doc = $self->resolveAnnotation($route);
                    $doc->middles = $route->middleware();
                    $self->docsCollect->push($doc);
                } catch (\ReflectionException $e) {
                    Logger::writeln('error ' . $e->getMessage());
                }
            });
        } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
            Logger::writeln('error ' . $e->getMessage());
        }

        return $static->docsCollect;
    }

    /**
     * @param RouteCollection $routes
     * @param $callback
     * @return void
     */
    protected function eachRoutes(RouteCollection $routes, $callback)
    {
        /**
         * @var Route $route
         */
        foreach ($routes as $route) {
            if ($this->isMatch($route)) {
                $callback($this, $route);
            }
        }
    }

    /**
     * @param Route $route
     * @return bool
     */
    protected function isMatch(Route $route): bool
    {
        if ($this->prefix && $prefixArray = explode(',', $this->prefix)) {
            if (!in_array($route->getPrefix(), $prefixArray)) {
                return false;
            }
        }

        $action = $route->getAction();

        if (!isset($action['controller'])) {
            return false;
        }

        return true;
    }

    /**
     * @param Route $route
     * @return Doc
     * @throws \ReflectionException
     */
    protected function resolveAnnotation(Route $route): Doc
    {
        Logger::writeln('parse ' . join('|', $route->methods()) . ' ' . $route->uri() . ' Annotation');

        $reader = new \Doctrine\Common\Annotations\AnnotationReader();

        $refClass = new \ReflectionClass(($route->getController()));

        $method = $refClass->getMethod($route->getActionMethod());

        $doc = new Doc();

        foreach ($this->annotations as $annot) {

            Logger::writeln('parse ' . join('|', $route->methods()) . ' ' . $route->uri() . ' method annotation ' . $annot);

            $annotation = $reader->getMethodAnnotation($method, $annot);

            $pro = Str::camel(class_basename($annot));

            if (!$annotation instanceof Annotation) {
                continue;
            }
            if (Str::endsWith($annot, 'Line')) {
                $doc->{$pro}[] = $annotation->toArray();
            } else {
                $doc->{$pro} = $annotation->toArray();
            }
        }

        return $doc;
    }

    /**
     * @return \Illuminate\Routing\RouteCollectionInterface|RouteCollection
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    protected function getRoutes()
    {
        /**
         * @var Router $router
         */
        $router = $this->application->get('router');

        return $router->getRoutes();
    }
}