今天看到php注解的介绍文章很感兴趣,动手实际试了试挺好玩,写这篇文章记录下
php从8开始支持原生注解功能了,我们可以写个小的例子看看注解怎么玩。
 
先确定我们的任务目标
1、编写一个注解类route处理根据注解反射将使用注解的方法加入到路由列表
2、定义入口文件初始化项目自动完成路由列表的更新
3、根据URL请求从路由列表获取路由信息找到对应类实例化并执行对应方法
 
先做个约定:
1、约定路由格式为:http://localhost/8/index.php?c=index&a=index
这里c 表示控制器controller,a 表示动作 action 也就是类方法
2、约定目录结构
root
|---index.php
|---config.php
|---route.php
---|controller
|---index.php
|---work.php
 
config.php配置文件,route.php注解路由类文件和index.php 入口文件平级
controller/indexController.php,controller/workController.php两个使用注解的测试控制器位于下一级目录controller中
 
前期准备
先准备两个类用来测试
<?php
class indexController
{
#[route('/controller/index','get')]
public function index():void
{
echo "This is attribute index controller \r\n";
} #[route('/controller/test','get')]
public function test():void
{
echo "This is attribute test controller \r\n";
}
}
indexController控制器类,有两个方法index和test
<?php
class workController
{
#[route('/controller/work','post')]
public function work():void
{
echo "This is attribute work controller \r\n";
}
}
workController控制器类,有一个方法work
 
前期工作完成,开始实际编写注解路由类
<?php
#[Attribute(Attribute::IS_REPEATABLE|Attribute::TARGET_METHOD)]
class route{ public static $all = []; public static $path = ''; public static $method = 'GET'; public static $function = ''; public static $controller = ''; public function __construct(){} public function setPath(string $path):self
{
$this->path = $path;
return $this;
} public function setMethod(string $method):self
{
$this->method = $method;
return $this;
} public function setFunction(string $function):self
{
$this->function = $function;
return $this;
} public function setController(string $controller):self
{
$this->controller = $controller;
return $this;
} public function addRoute():void
{
self::$all[str_replace("Controller","",$this->controller)][$this->function] = $this;
}
 
先定义路由的相关类属性,这里不考虑访问范围问题都用public修饰符
在这个类中定义了设置path、method、function、controller属性的方法,返回值用$this表示可以链式调用
addRoute() 方法将当前对应按 [‘控制器’][‘方法’] 的二维数组存起来备用
这里要说明下#[Attribute(Attribute::IS_REPEATABLE|Attribute::TARGET_METHOD)]
这就是注解的定义,有这个表示这个类是注解类Attribute是注解关键词,后面小括号里的是注解属性配置
IS_REPEATABLE 表示这个注解可以重复使用
TARGET_METHOD 表示这个注解只能用来修饰类内的方法
 
注意:这里定义的TARGET_METHOD在php语法检查时不会进行验证,即用这个注解修饰属性或类代码也不会报错,但执行会抛出异常
有了注解类我们就可以开始实际使用了
<?php
public static function setRoute($controllerClass){ $ref = new ReflectionClass($controllerClass);
$controller = $ref->getName();
$methods = $ref->getMethods(); foreach($methods as $method){
$function = $method->getName();
$attributes = $method->getAttributes(route::class); foreach($attributes as $attribute){ $route = $attribute->newInstance();
// 拿到注解上的参数
$params = $attribute->getArguments(); $route->setController($controller)
->setFunction($function)
->setPath($params[0])
->setMethod($params[1])
->addRoute();
}
}
}
}
在route类定义新的方法,根据注解反射路由列表setRoute()方法接收一个被route注解修饰的类作为参数。
通过反射获取控制器名称,方法列表将其调用route一系列方法设置属性并加入路由列表
 
现在我们可以使用注解生成路由列表了
indexController类的两个方法都使用route注解进行修饰,并指定path和method两个参数
此时我们打印route::$all路由列表

可以看到当前的路由列表确实是按照['控制器']['方法']的二维数组组装起来了,并且保存了每个路由的对象
到这里核心的使用注解生成路由列表其实已结束了,但如果不能看看效果谁知道你是不是胡说的呢?因此下面我们完善这个例子实现通过localhost/8/index.php?c=index&a=index格式的URL访问对应控制器的功能
 
<?php
class app{
public function appInit():self
{
if(glob("./controller/*.php")){
foreach(glob("./controller/*.php") as $fileName){
require_once($fileName);
$className = str_replace("./controller/","",str_replace(".php","",$fileName));
route::setRoute($className);
//route::$all;exit();
}
} return $this;
} public function run($config){
$controller = $_GET['c']??$config['default_controller'];
$action = $_GET['a']??$config['default_function']; if(isset(route::$all[$controller][$action])){
$route = route::$all[$controller][$action];
$className = $route->controller;
$function = $route->function;
(new $className)->$function();
}else{
exit("404 Not Found!");
}
}
}
这里我们定义一个app类作为项目的基本对象当前类中定义了两个方法
appInit()初始化项目并返回类对象
run() 根据对应的配置运行指定的路由和操作
为方便处理我这里直接遍历了controller目录,在实际的项目中应该使用自动加载机制按需加载
初始化时将controller目录下的全部控制器方法调用route类加入到路由列表
 
到这里我们只剩最后一步了,将请求过来的URL解析,找到controller 和 action 然后执行方法
这一步我们交给run()方法执行,
run()方法接收$_GET参数 c 和 a 分别对应 控制器和动作
判断如果路由列表中有对应的控制器和方法则将类实例化,调用对应方法,如果路由列表中没有这个对应路由则返回404错误。
需要指出的是这里404并不一定是没有这个对应方法,也有可能是有方法但没有使用route注解修饰导致反射找不到这个方法。

有此控制器和动作执行对应类对应方法

没有这个动作或控制器返回404
最后为了健壮性我们增加一个默认的控制器和默认动作,如果URL中没有传任何控制器和动作参数则走默认动作,将这个配置提取到配置文件中
<?php
$config = array(
'default_controller'=>'index',
'default_function'=>'index',
); return $config;
这样如果什么都不传我们的代码会默认访问index控制器下的index方法

贴上完整代码:
<?php
#[Attribute(Attribute::IS_REPEATABLE|Attribute::TARGET_METHOD)]
class route{ public static $all = []; public static $path = ''; public static $method = 'GET'; public static $function = ''; public static $controller = ''; public function __construct(){} public function setPath(string $path):self
{
$this->path = $path;
return $this;
} public function setMethod(string $method):self
{
$this->method = $method;
return $this;
} public function setFunction(string $function):self
{
$this->function = $function;
return $this;
} public function setController(string $controller):self
{
$this->controller = $controller;
return $this;
} public function addRoute():void
{
self::$all[str_replace("Controller","",$this->controller)][$this->function] = $this;
} public static function setRoute($controllerClass){ $ref = new ReflectionClass($controllerClass); $controller = $ref->getName(); $methods = $ref->getMethods(); foreach($methods as $method){
$function = $method->getName();
$attributes = $method->getAttributes(route::class); foreach($attributes as $attribute){ $route = $attribute->newInstance();
// 拿到注解上的参数
$params = $attribute->getArguments(); $route->setController($controller)
->setFunction($function)
->setPath($params[0])
->setMethod($params[1])
->addRoute();
}
}
}
}
route.php
 
<?php
class app{
public function appInit():self
{
if(glob("./controller/*.php")){
foreach(glob("./controller/*.php") as $fileName){
require_once($fileName);
$className = str_replace("./controller/","",str_replace(".php","",$fileName));
route::setRoute($className);
//route::$all;exit();
}
} return $this;
} public function run($config){
$controller = $_GET['c']??$config['default_controller'];
$action = $_GET['a']??$config['default_function']; if(isset(route::$all[$controller][$action])){
$route = route::$all[$controller][$action];
$className = $route->controller;
$function = $route->function;
(new $className)->$function();
}else{
exit("404 Not Found!");
}
}
}
init.php
 
<?php
class indexController
{
#[route('/controller/index','get')]
public function index():void
{
echo "This is attribute index controller \r\n";
} #[route('/controller/test','get')]
public function test():void
{
echo "This is attribute test controller \r\n";
}
}
indexController.php
 
<?php
class workController
{
#[route('/controller/work','post')]
public function work():void
{
echo "This is attribute work controller \r\n";
}
}
workController.php
 
<?php
$config = array(
'default_controller'=>'index',
'default_function'=>'index',
); return $config;
config.php
 
<?php
require_once('./config.php');
require_once('./route.php');
require_once('./init.php'); (new app)->appInit()->run($config);
index.php

php注解使用示例的更多相关文章

  1. MyBatis3-基于注解的示例

    在基于注解的示例中,可以简化编写XML的过程,全部采用注解方式进行编写,并在注解上写SQL语句,语句和XML的语句保持一致,并且可以省略掉XML文件不用引入的好处.但还有一点,基于注解的方式还没有百分 ...

  2. MyBatis多参数传递之注解方式示例--转

    原文地址:http://legend2011.blog.51cto.com/3018495/1015003 若映射器中的方法只有一个参数,则在对应的SQL语句中,可以采用#{参数名}的方式来引用此参数 ...

  3. spring注解controller示例

    依赖库 spring 3.0 配置web.xml文件如下: <?xml version="1.0" encoding="UTF-8"?> <w ...

  4. spring mvc 注解入门示例

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...

  5. Hibernate注解开发示例

    -------------------------------------------------------------------customer------------------------- ...

  6. Spring 注解学习 详细代码示例

    学习Sping注解,编写示例,最终整理成文章.如有错误,请指出. 该文章主要是针对新手的简单使用示例,讲述如何使用该注释,没有过多的原理解析. 已整理的注解请看右侧目录.写的示例代码也会在结尾附出. ...

  7. Spring bean依赖注入、bean的装配及相关注解

    依赖注入 Spring主要提供以下两种方法用于依赖注入 基于属性Setter方法注入 基于构造方法注入 Setter方法注入 例子: public class Communication { priv ...

  8. 基于注解的SpringMVC

    相比传统的继承Controller体系中某些类的方式,SpringMVC的注解具有以下优点: 1.Controller不再需要继承某个特定类,只是简单的POJO. 2.请求映射的配置非常方便灵活. 3 ...

  9. 【Java EE 学习 49 下】【Spring学习第一天】【MVC】【注解回顾】

    一.MVC 1.使用Spring有一个非常大的好处,那就是能够实现完全面向接口编程,传统的使用Dao.Service并不能实现完全的面向接口编程. 2.示例:https://github.com/kd ...

随机推荐

  1. Python自动化之常用模块学习

    自动化常用模块 urllib和request模块学习笔记 '获取页面,UI自动化校验页面展示作用': #-*- coding : utf-8 -*-import urllib.requestimpor ...

  2. 浅析websocket的基本应用spring boot + vue +C# + WPF

    1.基本概念 首先websocket是基于H5的一种通信.在网页中如果定时获取服务器端的实时数据,我们常采用long poll 和ajax轮询的方式.但是在轮询过程中,由于根本没有新数据的改变,而造成 ...

  3. 免费内网穿透服务Localtunnel

    Localtunnel 将为您分配一个唯一的可公开访问的 url,它将所有请求代理到您本地运行的网络服务器. 快速开始 全局安装 Localtunnel(需要 NodeJS)以使其在任何地方都可以访问 ...

  4. openstack中Nova组件简解

    一.Nova组件概述 计算节点通过Nova Computer进行虚拟机创建,通过libvirt调用kvm创建虚拟机,nova之间通信通过rabbitMQ队列进行通信. Nova位于Openstack架 ...

  5. Android同屏、摄像头RTMP推送常用的数据接口设计探讨

    前言 好多开发者在调用Android平台RTMP推送或轻量级RTSP服务接口时,采集到的video数据类型多样化,如420sp.I420.yv12.nv21.rgb的,还有的拿到的图像是倒置的,如果开 ...

  6. ProxySQL(11):链式规则( flagIN 和 flagOUT )

    文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9350631.html 理解链式规则 在mysql_query_rules表中,有两个特殊字段"fl ...

  7. Elasitcsearch7.X集群/索引备份与恢复实战

    文章转载自:https://mp.weixin.qq.com/s/_0RlojDsE30CeDSyLNP44w 1.问题引出 ES中文社区中,有如下问题: 问题1:存储数据,data目录从一个机器直接 ...

  8. 使用Elasticsearch的processors来对csv格式数据进行解析

    来源数据是一个csv文件,具体内容如下图所示: 导入数据到es中 有两种办法,第一种是在kibana界面直接上传文件导入 第二种方法是使用filebeat读取文件导入 这里采用第二种办法 配置文件名: ...

  9. [ML从入门到入门] 支持向量机:从SVM的推导过程到SMO的收敛性讨论

    前言 支持向量机(Support Vector Machine,SVM)在70年代由苏联人 Vladimir Vapnik 提出,主要用于处理二分类问题,也就是研究如何区分两类事物. 本文主要介绍支持 ...

  10. 微信小程序发布与支付

    一.小程序的发布流程 小程序协同工作和发布官网链接 1.背景 小程序的平台里,开发者完成开发之后,需要在开发者工具提交小程序的代码包,然后在小程序后台发布小程序. 2.流程 上传代码 代码管理服务器上 ...