php laravel的API版本管理相关类

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php laravel的API版本管理相关类相关的知识,希望对你有一定的参考价值。

# laravel的API版本管理相关类
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2019/5/13
 * Time: 16:16
 */

namespace App\Helper\Classes\ApiVersion;


use App\Helper\Traits\Singleton;

/**
 * api版本管理
 *
 * 分服务端版本和客户端版本,服务端版本就是api版本。
 *
 * 如果没有指定api版本或者提交的api版本低于当前最新api版本,就尝试检查是否有旧版本的代码
 *
 * 没有指定api版本时,需要后缀为_vEmpty
 * 指定版本时,需要后缀_v对应版本,可以指定父级版本
 */
class ApiVersion
{
    use Singleton;

    // 当前版本
    protected $version = '1.1.0';

    protected $headerVersionKey = 'api-version';

    protected $request;

    /**
     * 控制器目录名
     *
     * @var string
     */
    protected $controllerDirName = 'Version';

    /**
     * ApiVersion constructor.
     * @param $request
     */
    public function __construct($request = null)
    {
        $this->request = $request??request();
    }

    /**
     * 获取http里header的版本
     *
     * @return array|string|null
     */
    protected function getHeaderVersion()
    {
        return $this->getRequest()->header($this->headerVersionKey, '');
    }

    /**
     * @param bool $prefix
     * @return string
     */
    protected function getVersionString($prefix = false): string
    {
        return ($prefix ? 'v' : '') . str_replace('.', '_', $this->getVersion());
    }

    /**
     * @return string
     */
    protected function getVersion(): string
    {
        return $this->version;
    }

    /**
     * 判断当前版本是否大于客户端请求版本,如果true就访问对应版本的代码
     *
     * @return mixed
     */
    public function ifVersionCode()
    {
        return version_compare($this->getVersion(), $this->getHeaderVersion(), '>');
    }

    public function ifEmptyVersion()
    {
        return empty($this->getHeaderVersion());
    }

    /**
     * 修改为对应版本的控制器和方法
     */
    public function handleVersionCode()
    {
        if ($this->ifEmptyVersion()){ // 没有发送客户端版,就使用特定的空版本代码
            $this->handleEmptyVersionCode();
            return;
        }else if (!$this->ifVersionCode()){
            return;
        }

        $classname = $this->getVersionControllerDefaultNamespace();

        $controller = $this->handleControllerVersionCode($classname, $this->getHeaderVersionCodeNameArr($classname));


        // 存在要改变的控制器
        if ($controller !== false) {
            $route = $this->getRoute();
            $methodName = $route->getActionMethod();
            foreach (array_merge($this->getHeaderVersionCodeNameArr($methodName), [$methodName]) as $codeName) {
                if (method_exists($controller, $codeName)) { // 特定版本控制器,最精确匹配
                    $this->setRouteMethod($controller, $codeName);
                    continue;
                }
            }
        }
    }

    /**
     * 处理不传api版本
     *
     * 特定的空控制器和空方法都需要在后面带_empty
     */
    protected function handleEmptyVersionCode()
    {
        $classname = $this->getVersionControllerDefaultNamespace();

        $controller = $this->handleControllerVersionCode($classname, [$this->handleEmptyVersionName($classname)]);

        // 存在要改变的控制器
        if ($controller !== false) {
            $route = $this->getRoute();
            foreach ([$this->handleEmptyVersionName($route->getActionMethod())] as $codeName) {
                if (method_exists($controller, $codeName)) { // 特定版本控制器,最精确匹配
                    $this->setRouteMethod($controller, $codeName);
                    continue;
                }
            }
        }
    }

    protected function handleEmptyVersionName($name)
    {
        return $name . '_vEmpty';
    }

    /**
     * 通过路由类修改对应的控制器和方法
     *
     * @param $controllerInstance
     * @param $methodName
     */
    protected function setRouteMethod($controllerInstance, $methodName)
    {
        $this->getRoute()->controller = $controllerInstance;
        $this->getRoute()->uses(get_class($controllerInstance) . '@' . $methodName);
    }

    protected function handleControllerVersionCode($classname, $versionArr)
    {
        $controller = false;
        if (class_exists($classname)) { // 旧控制器
            $controller = app($classname);
        }else{ // 指定版本的控制器
            foreach ($versionArr as $codeName) {
                if (class_exists($codeName)) { // 特定版本控制器,最精确匹配
                    $controller = app($codeName);
                    continue;
                }
            }
        }

        return $controller;
    }

    /**
     * 获取控制器对应版本代码的默认命名空间
     *
     * @return mixed
     */
    protected function getVersionControllerDefaultNamespace()
    {
        $route = $this->getRoute();

        $replace = 'Controllers\\' . $this->getControllerDirName() . '\\';

        return str_replace('Controllers\\', $replace, get_class($route->getController()));
    }

    /**
     * 获取对接类名或者方法名的对应版本的名称
     *
     * @param $name
     * @return array
     */
    protected function getHeaderVersionCodeNameArr($name)
    {
        if (empty($this->getHeaderVersion())) {
            return [];
        }

        $result = [];
        $versionArr = $this->handleVersionStringToArr($this->getHeaderVersion());

        $result[] = $this->joinVersionItemToCode($name, $this->handleVersionPrefix($this->joinVersionItemToCode(...$versionArr)));
        array_pop($versionArr);
        $result[] = $this->joinVersionItemToCode($name, $this->handleVersionPrefix($this->joinVersionItemToCode(...$versionArr)));
        array_pop($versionArr);
        $result[] = $this->joinVersionItemToCode($name, $this->handleVersionPrefix($this->joinVersionItemToCode(...$versionArr)));

        return $result;
    }

    protected function handleVersionPrefix($version)
    {
        return 'v' . $version;
    }

    protected function joinVersionItemToCode(...$args)
    {
        return join('_', $args);
    }

    /**
     * 将字符串的版本信息改为数组
     *
     * @param $version
     * @return array
     */
    protected function handleVersionStringToArr($version)
    {
        $arr = explode('.', $version);
        foreach ($arr as &$item) {
            $item = intval($item);
        }

        return $arr + [0, 0, 0];
    }

    protected function getControllerDirName()
    {
        return $this->controllerDirName;
    }

    /**
     * @param string $controllerDirName
     * @return ApiVersion
     */
    public function setControllerDirName(string $controllerDirName)
    {
        $this->controllerDirName = $controllerDirName;

        return $this;
    }

    protected function getRequest()
    {
        return $this->request;
    }

    protected function getRoute()
    {
        return $this->getRequest()->route();
    }

    /**
     * @param mixed $request
     * @return ApiVersion
     */
    public function setRequest($request)
    {
        $this->request = $request;

        return $this;
    }

}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2019/5/13
 * Time: 16:47
 */

namespace App\Helper\Classes\ApiVersion;


class ApiVersionMiddleware
{
    public function handle($request, \Closure $next)
    {
        ApiVersion::instance()
            ->setRequest($request)
            ->handleVersionCode();

        return $next($request);
    }
}

以上是关于php laravel的API版本管理相关类的主要内容,如果未能解决你的问题,请参考以下文章

php laravel里缓存相关类库的修改

Laravel API 控制器结构?

使用版本控制实现 Laravel API 路由

[PHP相关教程] laravel5.1学习手册[一]基本开发环境配置

Laravel之控制器

laravel 旧版本不支持 php 8.0.8