1.获取某个key值 ArrayHelper::getValue()
return [
'tag' => ArrayHelper::getValue($headers, 'tag'),
'topic' => ArrayHelper::getValue($headers, 'topic'),
'requestId' => ArrayHelper::getValue($headers, 'request-id'),
'key' => ArrayHelper::getValue($headers, 'key'),
];
2.获取对应的value,有默认值。 ArrayHelper::get()
class ConsumerFactory
{
const ORDER_CREATE = 1;
const ORDER_CANCEL = 2;
protected static $map = [
self::ORDER_CREATE => OrderCreate::class,
self::ORDER_CANCEL => OrderCancel::class,
];
public static function create ($source) :ConsumerInterface {
$class = ArrayHelper::get(self::$map, $source, '');
return BeanFactory::getBean($class);
}
}
3.Bean理解
获取 Bean
@Inject
score
为 Bean::SINGLETON
级别的 bean
可以通过 @Inject
属性注入,底层会通过反射自动注入属性中。
Bean::PROTOTYPE
, Bean::REQUEST
bean不能使用 @Inject
注入
/**
* @Inject("config")
*
* @var Config
*/
private $config;
name 定义属性注入的bean名称。如果name为空,默认为@var 定义的类型。 这个name 可以是一个完整的类名,也可以是bean别名/bean名称。
如果 @Inject 没有指定 Bean 名称, 会自动根据 @var 的类型查找 Bean。
如果要使用 @Inject 属性注入必须有 类注解才可以,不然不会被解析。
BeanFactory
BeanFactory
提供了一种先进的配置机制来管理任何种类的bean。
获取score
为Bean::SINGLETON
,Bean::PROTOTYPE
可以使用下面,以获取 wsRouter
为例:
$router = Swoft::getBean('wsRouter');
$router = BeanFactory::getBean('wsRouter');
$router = Container::$instance->get('wsRouter')
$router = BeanFactory::getContainer()->get('wsRouter');
$router = bean('wsRouter'); //通用函数
上述这几种方式都可以获取Bean::SINGLETON
或Bean::PROTOTYPE
类型的bean
4.model类获取表名
public function getTableName($class, $companyId)
{
$o = bean(Proxy::getClassName($class)); //理解为 获取到类名
//设置@Entity 实体,可以获取到表名
return $o::tableName() . '_' . $companyId % Constant::TABLE_COUNT;
}
5.swoft多类异常捕捉
try {
$body = $request->input();
$rlt = $this->_checkListService->getCompanyCheckList($params);
} catch (Swoft\Db\Exception\DbException $e1) {
Log::error("get_checklist_db_exception, code: %d, err:%s,file:%s, line: %d", $e1->getCode(), $e1->getMessage(), $e1->getFile(), $e1->getLine());
} catch (Swoft\Redis\Exception\RedisException $e2) {
Log::error("get_checklist_redis_exception, code: %d, err:%s,file:%s, line: %d", $e2->getCode(), $e2->getMessage(), $e2->getFile(), $e2->getLine());
} catch (Swoft\Exception\SwoftException $e3) {
Log::error("get_checklist_swoft_exception, code: %d, err:%s,file:%s, line: %d", $e3->getCode(), $e3->getMessage(), $e3->getFile(), $e3->getLine());
} catch (\Exception $e) {
Log::error("get_checklist_exception, code: %d, err:%s,file:%s, line: %d", $e->getCode(), $e->getMessage(), $e->getFile(), $e->getLine());
}
6.获取redis实例,以及使用方式
Redis::connection('redis.clusters-pool')->get('key');
7.mysql,redis,http各类I/O调用 协程并发
$result = Co::multi([
'sku_purchase' => function() use($params, $skuIds) {
return $this->_skuPurchaseStatisticDao->getSkuPurchaseTimes($skuIds, $params['company_id']);
},
'recommend_data' => function() use($params, $skuIds) {
return $this->_rpcCompanySku->getRecommend($params['city_id'], $params['area_id'], $skuIds, $params['longitude'], $params['latitude'], $params['fulfillment'], $params['warehouse_ids'], $params['channel']);
},
'saleclass1' => function() use($skuIds) {
return $this->_rpcGoodsService->getSkuSaleClass1BySkuIds($skuIds);
},
'parent_saleclass' => function() {
return $this->_rpcGoodsService->getParentSaleClass();
}
]);
return [
$result['sku_purchase'],
$result['recommend_data'],
$result['saleclass1'],
$result['parent_saleclass'],
];
list($skuPurchase, $companySkuData, $saleClassData, $parentSaleClass) = $this->_concurrentGet($params, $skuIds);
8.sgo() 协程并发
一些长耗时并不需要返回结果的操作可以采用单起协程来处理,不需要关心处理结果,这里之所以不适用异步task来做,是因为task进程处理时间长会阻塞worker进程的请求,容易出问题。task进程适合一般耗时的异步任务等情况。
//数据统计
sgo(function() use ($params, $res) {
$this->_setSkuStatistic($params, $res);
});
父协程不会等待子协程的执行结果,当然也可以等待子协程的返回结果,使用defer模式或者channel通道模式,sgo()采用channel模式。
9.AOP切面类
项目中可以采用Aspect 来编写切面的方法,并且定义需要进行切面的控制器和方法,可以用来做服务降级通用配置等 类似公共访问处
10.Listener监听类
设置监听swoole各个阶段的事件,例如@Listener(event=SwooleEvent::WORKER_START)
监测在swoole的worker进程启动时,调用此方法,例如增加saberGm的处理方法,增加请求后的日志记录和异常的捕捉。
11.Process用户自定义进程池
bean.php加入配置后,
'processPool' => [
'class' => \Swoft\Process\ProcessPool::class,
'workerNum' => 1
],
@Process(workerId=0)
执行run方法,里面自行实现 while(true)
可以用来执行脚本任务等,单次跑,多次跑都可以。
12.Validator
①定义@Validator("PurchaseValidator")验证器,里面有具体验证属性的规则
/**
* @IsInteger(message="skuId必须是整型")
*/
protected $skuId;
②当然也可以自定义规则,需要用到Annotation解析,实例:
//自定义一个规则
/**
* @IsList(message="items必须是数组", fields={
* "biId:integer",
* "class1Code:integer",
* "brand:string",
* "level:string",
* "flavor:string",
* "sequence:integer",
* "dataSource:integer",
* "searchWord:string",
* "ssuId:integer",
* "skuId:integer:required",
* "ssuFp:integer"
* })
*/
protected $items;
规则文件:
message = $values['value'];
}
if (isset($values['message'])) {
$this->message = $values['message'];
}
if (isset($values['name'])) {
$this->name = $values['name'];
}
if (isset($values['fields'])) {
$fields = [];
foreach ($values['fields'] as $field) {
$field = explode(":", $field);
$fields[$field[0]] = [
'type' => $field[1],
'required' => isset($field[2]) && $field[2] ? 1 : 0
];
}
$this->fields = $fields;
}
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
public function getFields(): array
{
return $this->fields;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
}
解析文件:
className, $this->propertyName, $annotationObject);
return [];
}
}
③自定义验证器,在用到的控制器和方法上注解进去。
$optionValue ) {
if (!in_array($option, $optionKeys) ) {
throw new ValidatorException("options选项${option}未定义");
}
if ( !is_bool($optionValue) ) {
throw new ValidatorException("options选项${option}必须是bool类型");
}
}
return $options;
}
}
④控制器中使用,在对应方法需要验证加上
@Validate(validator="PurchaseValidator", fields={"skuId"})
⑤公共方法validate()
function validate(array $data, string $validatorName, array $fields = [], array $userValidators = []): array
全局函数使用,当验证器失败会抛出 Swoft\Validator\Exception\ValidatorException 异常
$data
需要验证的数据,必须是数组 KV 格式
$validatorName
使用的验证器( @Validator() 注解标记的 )
$fields
需要验证的字段,为空验证器所有字段
$userValidators
同时使用的自定义验证器,支持两种格式。
validate($request->getParsedBody(), 'CommonValidator',[], ['OptionsValidator']);
13.中间件Middleware
①配置全局中间件
当你的自定义中间件需要全局请求应用,则可以考虑将此中间件作为全局中间件去使用,只需在 Bean 配置文件内配置 httpDispatcher 的 middlewares 属性,在数组中加入你的自定义中间件的命名空间地址,相关配置通常在 app/bean.php 内
return [
...
'httpDispatcher'=>[
'middlewares'=>[
AuthMiddleware::class,
ApiMiddleware::class
]
]
...
];
然后所有请求会先经过中间件的process方法,这里可以做一些过滤,争对请求或者是用户鉴权,统一返回值结构等
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$path = $request->getUri()->getPath();
if ($path === '/favicon.ico') {
$response = Context::get()->getResponse();
return $response->withStatus(404);
}
if ( !in_array($path, ['/status']) ) {
validate($request->getParsedBody(), 'CommonValidator',[], ['OptionsValidator']);
}
/**
* @var \App\Common\EnvParams
*/
$envParams = EnvParams::getEnvPamrams();
$envParams->set($request->getParsedBody());
$response = $handler->handle($request); //具体的方法执行
return $this->getStandResponse($response);
}
②也可以通过注解使用
通过 @Middleware
和 @Middlewares
, 可以很方便的配置中间件到当前的 Controller 和 Action 内
- 当将此注解应用于
Controller
上,则作用域为整个Controller
- 将此注解应用于
Action
上,则作用域仅为当前的Action
-
@Middleware
用于配置单个中间件 -
@Middlewares
显而易见的是用于配置一组@Middleware
,按照定义顺序依次执行
14.获取别名目录
$path = Swoft::getAlias('@base');
$path = alias('@base')