Gitlib Gitlib
首页
  • 分类
  • 标签
  • 归档
  • Golang开发实践万字总结
  • MySQL核心知识汇总
  • Redis实践总结
  • MQ实践万字总结
  • Docker数据持久化总结
  • Docker网络模式深度解读
  • 常用游戏反外挂技术总结
  • 读书笔记
  • 心情杂货
  • 行业杂谈
  • 友情链接
关于我
GitHub (opens new window)

Ravior

以梦为马,莫负韶华
首页
  • 分类
  • 标签
  • 归档
  • Golang开发实践万字总结
  • MySQL核心知识汇总
  • Redis实践总结
  • MQ实践万字总结
  • Docker数据持久化总结
  • Docker网络模式深度解读
  • 常用游戏反外挂技术总结
  • 读书笔记
  • 心情杂货
  • 行业杂谈
  • 友情链接
关于我
GitHub (opens new window)
  • PHP

    • PHP-FPM使用指南
    • PHP7新特性总结
    • PHP安全编程
    • PHP安全配置总结
    • PHP变量的值类型和引用类型
    • PHP标准规范PSR
    • PHP操作Zookeeper实践
    • PHP错误和异常处理机制详解
    • PHP的Session运行机制
    • PHP底层运行机制和原理
    • PHP反射模拟实现注解路由
      • 反射
      • 注解路由实现
      • 弊端
      • 参考文档
    • PHP高级用法总结
    • PHP开发常用文档总结
    • PHP开发入门:Memcached扩展安装
    • PHP开发入门:PHP7安装部署
    • PHP开发入门:Redis扩展安装
    • PHP开发SPL总结
    • PHP框架常见URL模式
    • PHP扩展开发入门
    • PHP垃圾回收机制
    • PHP类的自动加载
    • PHP输入输出流
    • PHP微服务开发指南
    • PHP协程
    • PHP写时拷贝技术
    • PHP性能优化之Opcache
    • PHP依赖注入和控制反转
    • PHP运行模式(SAPI)
    • PHP中file_get_contents与curl区别
    • RPC的简单实现
    • Protobuf:高效数据结构化工具
    • P3P协议详解
    • Laravel之集合(Collection)总结
    • Laravel实践总结
    • Laravel之ORM总结
    • 中高级PHP实践总结
    • PHP Socket编程实战
  • Golang

  • Python

  • Javascript

  • 其他语言

  • 编程语言
  • PHP
Ravior
2017-11-13
目录

PHP反射模拟实现注解路由

在阅读TP框架源码中,发现TP框架支持使用注解方式定义路由(也称为注解路由),类似于Java中注解,默认关闭,如果需要开启在路由配置文件中设置:

// 开启注解路由
'route_annotation'       => true,
1
2

然后只需要直接在控制器类的方法注释中定义,例如:

<?php
namespace app\controller;

class Index
{
    /**
     * @param  string $name 数据名称
     * @return mixed
     * @route('hello/:name')
     */
	public function hello($name)
    {
    	return 'hello,'.$name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@route('hello/:name') 就是注解路由的内容,然后就使用下面的URL地址访问:

http://tp5.com/hello/thinkphp

页面输出:

hello,thinkphp

注解路由使路由定义更为方便,但PHP本身没有类似于Java的Annotation机制,那是如何实现的呢?这就不得不说到PHP的反射机制。

# 反射

PHP自5.0版本以后添加了反射机制,它提供了一套强大的反射API,允许你在PHP运行环境中,对类、接口、函数、方法和扩展进行逆向分析,经常用于高扩展的PHP框架,自动加载插件,自动生成文档等。

在TP5框架中,注解路由都是基于PHP反射来实现,大概思路如下:

  1. 通过反射类获取类的所有public方法;
  2. 分析每个public方法的文档注释,如果含有特定路由标志,则将该方法作为路由处理函数

# 注解路由实现

下面,我们模拟实现注解路由。先定义一个控制器类 controller/User.php:

<?php

class User
{

  /**
	 * @route('/user/login')
	 * @return [type] [description]
	 */
  public function login($data) 
  {

    echo "login: ". $data['name'];
  }

  /**
	 * @route('/user/info')
	 * @return [type] [description]
	 */
  public function info($data) 
  {

    echo "info: ". $data['name'];
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

在User控制器中,定义了两个方法,对应两条路由@route('/user/login') 和 @route('/user/info')。从语法层面来讲,这些都只是普通的文档注释,但是我们可以通过解析路由标志和控制器方法,将其关联起来,那我们就可以在特定路由下调用特定控制器方法来处理请求。例如:当请求index.php?s=/user/login时会执行User控制器中login方法。

通常情况下,我们都会将控制器类都统一放在某一个目录下,如controller, 我们分析controller目录下每个控制类中的public方法的文档注释,将含有@route路由标志的方法和路由信息关联起来。

<?php
define('PHP_EXTENSION', '.php');

spl_autoload_register(function($className) {
	require_once('controller'.DIRECTORY_SEPARATOR.$className.PHP_EXTENSION);
});

function initRoute()
{
	$routes = [];
	$controllers = glob('controller/*.php');
  // 分析controller目录下所有控制器类
	foreach ($controllers as $controller) {
		$className = explode('/', $controller)[1];
		$className = explode('.', $className)[0];

		$classRef = new ReflectionClass($className);
		$classMethods = $classRef->getMethods(ReflectionMethod::IS_PUBLIC);
		foreach ($classMethods as $classMethod) {
      // 分析路由标志
			preg_match('/@route\(\'([\w\/]*)\'\)/', $classMethod->getDocComment(), $matches);
			if (count($matches) > 1) {
				$routes[$matches[1]] = [
					'c' => $classRef->newInstance(),
					'm' => $classMethod
				];
			}
		}
	}

	return $routes;
}

$routes = initRoute();
$path = explode('?', $_GET['s']);
$uri = $path[0];
parse_str($path[1], $args);


foreach ($routes as $key => $value) {
	if ($key == $uri) {
		$value['m']->invoke($value['c'], $args);
	}
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

就这样,我们基本上模拟出了TP框架(URL兼容模式)下的注解路由实现。

# 弊端

Java由于本身语言特性导致非常适合用注解方式来开发,但PHP本身如果用注解路由,不得不使用反射。用反射的话,那性能自然就降低,不得不用cache(缓存路由信息,不然每次都得分析注解),同时也不便于统一管理URl,所以在TP框架中注解路由并不是默认开启。


# 参考文档

  • ReflectionAPI (opens new window)
#PHP
上次更新: 2022/12/01, 11:09:34
PHP底层运行机制和原理
PHP高级用法总结

← PHP底层运行机制和原理 PHP高级用法总结→

最近更新
01
常用游戏反外挂技术总结
11-27
02
Golang开发实践万字总结
11-11
03
Redis万字总结
10-30
更多文章>
Theme by Vdoing | Copyright © 2011-2022 Ravior | 粤ICP备17060229号-3 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式