hyperf框架的入门到精通

Posted mr.杰瑞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hyperf框架的入门到精通相关的知识,希望对你有一定的参考价值。

前言

我跳槽了,去了一家电商。
人家都是docker开发,用的框架是hyperf,然后入职第一天,搭了一天环境。
这就是学习的动力,开始吧!加油打工人!!!

数据库配置 (读写分离)

  • Coroutine\\mysql
    协程MySQL客户端,但是人家不推荐使用了,现在推荐使用的是Swoole\\Runtime::enableCoroutine+PDO或Mysqli方式,即一键协程化原生php的MySQL客户端
  • hyperf/db-connection组件
    基于hyperf/pool实现数据库连接池并对模型进行了新的抽象,以它作为桥梁,hyperf才能把数据库组件及事件组件接进来

composer require hyperf/db-connection
其实composer.json已经有
“hyper/db-connection”:“XXX”,
所以可以忽略这条

database配置在哪?
在config/autoluad/databases.php

如何配置读写分离?

普通

return [
	'dafault'=>[
		'driver'=>env('DB_DRIVER','mysql'),
		'host'=>env('DB_HOST','localhost'),
		'database'=>env("DB_DATABASE",'hyperf'),
		'port'=>env("DB_PORT",'3306'),
		....
		]
]

读写分离
主要就三个参数 read write sticky

return [
	'dafault'=>[
		'driver'=>env('DB_DRIVER','mysql'),
		'read'=>[
			'host'=>['192.168.1.1'],
		],
		'write'=>[
			'host'=>['192.168.1.2'],
		],
		'sticky'=>true,
		'database'=>env("DB_DATABASE",'hyperf'),
		'port'=>env("DB_PORT",'3306'),
		....
		]
]

.env文件里面放一些 数据库的配置

DB_DRIVER =mysql
DB_HOST =127.0.0.1
DB_PORT=3306
DB_DATABASE=hyperf
DB_USERNAME=root

创建表

php bin/hyper.php gen:model 表名

增加

获取请求的值和响应 hyperf提供了
Hyperf\\HttpServer\\Contract\\RequestInterface和Hyperf\\HttpServer\\Contract\\ResponseInterface

public function save(RequestInterface $request)
{
	$user = $request->query('username');
	$res = Db::table('user')->insert(
		['username'=>$username]
	);
	return [
	'res'=>$res
	];
}

删除

public function del(RequestInterface $request)
{
	$id=(int)$request->query('id');
	$res = Db::table('user')->where('id','=',$id)->delete();
	return [
	'res'=>$res
	];
}

查询

ps:alt + ctrl 点鼠标 能把缺少的包引入
Query查询类
use Hyperf\\DbConnection\\Db

查单条

public function getOne()
{
	$user = Db::select("select * from `user` where id=?",[1]);//返回array
	//或者
	User::query()->where('id',1)->get()->toarray();
	return $user;
}

查多条

public function getList()
{
	$users = Db::select("select * from `users` where id>=? and id<=?",[0,100])
	return $users;
}

路由

注解路由

顾名思义就是注解里面有,路由的信息,代码说一下

/**
* @return array
* @GetMapping(path="index3",methods="get,post")
*/
public function index3()
{
	return '123';
}

会生成一条域名/index/index3

ps:有时候,有些代码是灰的,用ctrl 鼠标点一下,建议把什么包引入,点完就发现有个包引入了use Hyperf\\HttpServer\\Annotation\\RequestMapping;

注解路由首先是控制器的名 然后是方法名

<?php
declare(strict_types=1);

namespace App\\Controller;

use Hyperf\\HttpServer\\Contract\\RequestInterface;
use Hyperf\\HttpServer\\Annotation\\Controller;
use Hyperf\\HttpServer\\Annotation\\RequestMapping;

/**
 * @Controller()
 */
class UserController
{
    // Hyperf 会自动为此方法生成一个 /user/index 的路由,允许通过 GET 或 POST 方式请求
    /**
     * @RequestMapping(path="index", methods="get,post")
     */
    public function index(RequestInterface $request)
    {
        // 从请求中获得 id 参数
        $id = $request->input('id', 1);
        return (string)$id;
    }
}

route文件的路由

get post head首先了解一下子
config/routes.php文件配置

//意思是只能通过get获取 域名/index 访问到app/controller/IndexController 的index方法   这是方法一
Router::get('/index','App\\Controller\\IndexController::index');
//还可以用@
Router::get('/index','App\\Controller\\IndexController@index');
//还可以用数组方式
Router::get('/index',[App\\Controller\\IndexController::class,'index']);

//注册任意HTTP METHOD路由
Router::addRoute(['GET','POST','HEAD'],'/','App\\Controller\\IndexController@index')
//闭包定义路由
Router::get('/hello',function (){
    return 'hello';
});
//还有一种方式偷懒方法  路由组
Router::addGroup('/user/',function(){
    Router::get('index',[App\\Controller\\UsersController::class,'index']);
    Router::post('info',[App\\Controller\\UsersController::class,'info']);
});

//注册与方法名一致的HTTP METHOD的路由
Router::get($uri,$callback);
Router::post($uri,$callback);
Router::put($uri,$callback);
Router::patch($uri,$callback);
Router::delete($uri,$callback);
Router::head($uri,$callback);

//注解路由 注意三个要点
//1.不要在routes.php文件中增加相关配置
//2.use Hyper\\HttpServer\\Annotation\\AutoController;
//3.添加以下的注释  
/**
* @AutoController()
*/
//访问 是通过 域名+/控制器名/+方法名访问
//如果不想用控制器名的话用下面的方法
/**
* @AutoController(prefix="user")
*/
//此时访问 是通过 域名+/user/+方法名 访问

//如果是增加了一层user目录 ,注解是下面这样写的,那怎么访问呢
/**
* @AutoController()
*/
//此时访问是 域名+/user/+/控制器名/+方法名

//ps:plugins 安装php annotations的扩展  就有自动引入的功能了 方便极了


//还有种注解形式配置路由,@Controller 需要搭配 @RequestMapping,@GetMapping,@PostMapping,@PutMapping 等注释一起使用

/**
* @Controller(prefix="index")
*/
class IndexController extends AbstractController
{
	/**
	* @RequestMapping(path="index", methods={"get","post"})
	*/
	public function index()
	{
		return 1;
	}
}
//地址访问 域名+/index/+index

//有个小技巧 如果遇到一种情况,不想用注解里面的index,可以将
@RequestMapping(path="/over",methods={"get","post"})

//地址访问 域名+/over

//@GetMapping(path="/over")  get访问 域名+/over

在Controller目录下新建IndexController控制器

<?php
namespace App\\Controller;


class IndexController extends AbstractController
{

    public function index()
    {
        return 'hi';
    }


}

UsersController.php

<?php
namespace App\\Controller;


class UsersController extends AbstractController
{


    public function index()
    {
        return 'index';
    }


    public function info()
    {
        return 'info';
    }


}

hyperf的依赖注入

hyperf里面用到了ioC(控制反转) DI(依赖注入)
容器将依赖注入对象,对象实例化通过容器自动得到外部依赖

由hyperf/di组件提供功能支持

注入方式

通过构造方法注入

namespace App\\Service
class userService
{
	public function getInfoById(int $id)
	{
		return (new Info())->fill($id);
	}
}


如果其他的类想要用引入这个类的话,应该怎么办呢?

namespace App\\Controller;
use App\\Service\\UserService;
class IndexController
{
	/**
	* @var UserService
	*/
	private $userService;
	//通过构造函数的参数上声明参数类型完成自动注入
	public function __construct(UserService $userService)
	{
		$this->userService = $userService;
	}
	
	public function index()
	{
		//此时可以直接使用了
		return $this->userService->getInfoById(1);
	}
}

通过@inject注解注入

namespace App\\Controller;
use App\\Service\\UserService;
use Hyperf\\Di\\Annotation\\Inject; //这个要引入
class IndexController
{
	/**
	* 通过'@Inject' 注释注入由 '@var' 注解声明的属性类型对象
	*
	* @Inject               
	* @var UserService
	*/
	private $userService;
	public function index()
	{
		//直接使用
		return $this->userService->getInfoById(1);
	}
}

注入类型

简单对象注入

抽象对象注入

namespace App\\Service;
interface UserServiceInterface
{
	public function getInfoById(int $id);
}

class UserService implements UserServiceInterface
{
	public function getInfoById(int $id)
	{
		return (new Info()->fill($id));
	}
}

还没完,要在对应的位置进行接口类与实现类的关系绑定

use App\\Service\\UserServiceInterface;
use App\\Service\\UserService;
//在config/dependencies.php内完成关系配置
return [
	'dependencies'=>[
	UserServiceInterface::class=>UserService::class
	],
];

controller 怎么使用?

namespace App\\Controller;
use App\\Service\\UserServiceInterface;
use Hyperf\\Di\\Annotation\\Inject;
class IndexController
{
	/**
	* @Inject
	* @var UserServiceInterface
	*/
	private $userService;
	public function index()
	{
		return $this->userService->getInfoById(1);
	}
}

工厂对象注入

namespace App\\Service;
class UserService implements UserServiceInterface
{
	/**
	* @var bool
	*/
	private $enableCache;
	public function __construct(bool $enableCache)
	{
		//接收值并储存于类属性中
		$this->enableCache = $enableCache;
	}
	public function getInfoById(int $id)
	{
		return (new Info()->fill($id));
	}
}

新建一个工厂类

namespace App\\Service;
use Hyperf\\Contract\\ConfigInterface;
use Psr\\Container\\ContainerInterface;
class UserServiceFactory
{
	//实现一个 __invoke()方法来完成对象的生产
	//方法参数会自动注入一个当前的容器实例
	public function __invoke(ContainerInterface $container)
	{
		$config = $container->get(ConfigInterface::class)
	};
	//我们假设对应的配置的key 为cache.enable
	$enableCache = $config->get('cache.enable',false);
	//make(string $name,array $paramters=[])方法
	//等同于 new,使用make()方法时为了允许AOP的介入 而直接new会导致AOP无法正常介入流程
	return make(UserService::class,compact('enableCache'));
}

定义一个工厂类,于__invoke()方法内实现对象的创建并返回
make()函数创建短声明周期对象
ps: compact 函数是取 enableCache的变量的值 就是。

array('enableCache'=>$enableCache); 
//compact 就是取的$enableCache;

这还没完,别忘了在配置里面加

use App\\Service\\UserServiceInterface;
use App\\Service\\UserServiceFactory;
//config/dependencies.php
return [
	'dependencies'=>[
UserServiceInterface::class=>UserServiceFactory::class
	],
];

还可以注入容器本身

直接注入 Psr\\Container\\ContainerInterface

通过Hyperf\\Utils\\ApplicationContext::getContainer()获得

注意DI容器创建出来的对象是个单例,是个长生命周期的对象,通过$container->make()方法或者make()函数创建短生命周期对象

自定义注解

namespace App\\Annotation;
use Hyper\\Di\\Annotation\\AnnotationInterface;

/**
* @Annotation
* @Target({"CLASS","METHOD"})
*/
class Foo implements AnnotationInterface
{
	public function collectClass(string $className):void
	{
	
	}
	public function collectMethod(string $className, $string $target):void
	{
	
	}
	public function collectProperty(string $className, $string $target):void
	{
	
	}
}

//其实不用这么写  因为AbstractAnnotation 已经帮我们做了,用下面的写法

namespace App\\Annotation;
use Hyperf\\Di\\Annotation\\AbstractAnnotation;
/**
* @Annotation
* @Target({"CLASS","METHOD"})
*/
class Foo extends AbstracAnnotation
{
	/**
	* 假如想接收一下属性
	* @var string
	*/
	public $bar;

	/**
	* @var string
	*/
	public $bab;
	
	public function __construct($value = null)
	{
		var_dump($value);
		parent::__construct($value);
		//如果bab是一个可选参数用下面的写法,控制器文件注解那里@Foo(bar="123", bab="321") 换成 @Foo(123)
		$this->bindMainProperty('bar',$value);
	}

	public function collectClass(string $className):void
	{
		parent::collectClass($className);
	}
	
	
}


怎么用?

namespace App\\Controller;

use App\\Annotation\\Foo;
use Hyperf\\HttpServer\\Annotation\\AutoController;

/**
* @AutoController()
* @Foo(bar="123", bab="321")
*/
class IndexController
{
	public function index()
	{
		var_dump(AnnotationCollector::getClassByAnnotation(Foo::class));
		return 1;
	}
}

//打印结果是个数组




AOP是什么

  • 面向切面编程,通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术
  • AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果

每个方法的前后就是切面。
Hyperf的AOP是基于DI实现的
必须使用的是Hyperf的DI组件,即hyperd/di
必须通过DI创建的对象才能使AOP生效,直接new不行
必须当代理类缓存文件不存在时才会重新生成代理类

AOP的应用场景

  • 参数校验,日志,无侵入埋点,安全控制,性能统计,事务处理,异常处理,缓存,无侵入监控,资源池,连接池管理等
    例子

  • 假设有一个IndexController类

  • 通过AOP对该类切入

  • 创建一个Aspect类,并在$classes属性定义该类的index方法

class IndexController
{
	public function index(RequestInterface $request)
	{
		return 'hello';
	}
}
namespace App\\Aspect;
class FooAspect extends AbstractAspect
{
	//要切入的类,可以多个,也可以通过::标识到具体的某个方法,通过*可以模糊匹配
	public $classes = [
		SomeClass::class,
		'App\\IndexController::index',
	];
}

Aspect 实际通过DI得到的类为下图的一个代理类,储存于runtime/container/proxy
是一个继承原类的子类,原类方法会被重写

class IndexController_sdhafkjshdladjla8374 extends IndexController
{
	use \\Hyper\\Di\\Aop\\ProxyTrait;
	public function index(RequesInterface $request)
	{
		$__function__ = __FUNCTION__;
		$__method__ = __METHOD__;
		return self::__proxyCall(IndexController::class,__FUNCTION__,self::getParamsMap(IndexController::class,__FUNCTION__,func_get_args(),function(RequestInterface $request) use($__function__,$__method__){
		});
	}
}

基于AOP的功能

  • @Cacheable(通过这个注解定义在任意类的方法上,可以对,对应方法的结果缓存) @CachePut(更新缓存) @CacheEvict(移除缓存)
  • @Task 在任意类的任意方法上定义Task注解 ,表明在调用方法时,将任务分配到task work里面执行,意思是假如有同步阻塞的代码,要在协程里面运行,就能用task
  • @RateLimit 限流的注解
  • @CircuitBreaker 熔断器
  • 调用链追踪的无侵入埋点

完整例子

对目标indexController.php前后添加业务逻辑

namespace App\\Controller;
use Hyper\\HttpServer\\Annotation\\AutoController;

/**
* @AutoController()
*/
class IndexController
{
	public function index()
	{
		return 2;
	}
}

1.首先在app下面新建一个文件夹,Aspect
2.新建一个方法indexAspect.php

namespace App\\Aspect;
use Hyperf\\Di\\Aop\\AbstractAspect;    //这里
以上是关于hyperf框架的入门到精通的主要内容,如果未能解决你的问题,请参考以下文章

Atom编辑器入门到精通 Atom使用进阶

Atom编辑器入门到精通 Atom使用进阶

Atom编辑器入门到精通 Atom使用进阶

Atom编辑器入门到精通 Atom使用进阶

shader从入门到精通——Shader的组织形式

Flask框架从入门到精通之模板宏(十九)