在Swoole环境下运行注入Yii2框架的thrift应用

前两天发布了使用swoole来运行thrift应用,项目虽然可以运行起来,但是周边的生态(如缓存,ORM,日志等等)并没有跟上,实际上开发体验比较差。周末研究了一下,把Yii2框架集成到了thrift应用上。

项目地址:https://github.com/swoole-foundation/yii2-swoole-thrift

Yii2优势:

在swoole上运行Yii2应用

Yii2:业界著名的开发框架,完美的OOP设计以及组件化开发思想保证了框架的扩展性。
Swoole:面向生产环境的 PHP 异步网络通信引擎。使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。

Yii2优点

  • 完美的OOP设计

使用yii2依赖注入规范业务开发

本文代码

https://github.com/xialeistudio/yii2-di-demo

什么是依赖注入(DI)?

对象由框架来创建而不是程序员通过 new 创建。跟IoC差不多一个意思。

为什么要有依赖注入?

yii2关闭csrf校验和cookie校验

重要提示

1
关闭该选项会导致应用安全性收到影响!

问题出现

  1. 开发API的时候发现POST请求老是不能通过验证,直接把报错文案放到项目中去搜索发现yii\web\Request中有enableCsrfValidation

Yii2框架MemCache在腾讯云部署时不过期问题

之前部署在阿里云时一直memcache没有问题,部署到腾讯云发现缓存永不过期。查看yii2的MemCache类源码后,发现在设置缓存时,Yii2添加了$expire = $duration > 0 ? $duration + time() :0;这样的代码,会导致强制使用时间戳来标记过期时间,而阿里云是过期时间和时间戳都支持的,腾讯云只支持前者。

解决方案

重写组件即可
app\components中加添加MemCache集成Yii2的MemCache,由于Yii2的MemCache中$_cache成员变量为private,子类无法访问,所以需要直接copy源码:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
<?php
/**
* Created by PhpStorm.
* User: xialei
* Date: 2017/5/12
* Time: 下午5:55
*/

namespace app\components;


use yii\base\InvalidConfigException;
use yii\caching\MemCacheServer;

class MemCache extends \yii\caching\MemCache
{

/**
* @var bool whether to use memcached or memcache as the underlying caching extension.
* If true, [memcached](http://pecl.php.net/package/memcached) will be used.
* If false, [memcache](http://pecl.php.net/package/memcache) will be used.
* Defaults to false.
*/
public $useMemcached = false;
/**
* @var string an ID that identifies a Memcached instance. This property is used only when [[useMemcached]] is true.
* By default the Memcached instances are destroyed at the end of the request. To create an instance that
* persists between requests, you may specify a unique ID for the instance. All instances created with the
* same ID will share the same connection.
* @see http://ca2.php.net/manual/en/memcached.construct.php
*/
public $persistentId;
/**
* @var array options for Memcached. This property is used only when [[useMemcached]] is true.
* @see http://ca2.php.net/manual/en/memcached.setoptions.php
*/
public $options;
/**
* @var string memcached sasl username. This property is used only when [[useMemcached]] is true.
* @see http://php.net/manual/en/memcached.setsaslauthdata.php
*/
public $username;
/**
* @var string memcached sasl password. This property is used only when [[useMemcached]] is true.
* @see http://php.net/manual/en/memcached.setsaslauthdata.php
*/
public $password;

/**
* @var \Memcache|\Memcached the Memcache instance
*/
private $_cache;
/**
* @var array list of memcache server configurations
*/
private $_servers = [];


/**
* Initializes this application component.
* It creates the memcache instance and adds memcache servers.
*/
public function init()
{
parent::init();
$this->addServers($this->getMemcache(), $this->getServers());
}

/**
* Add servers to the server pool of the cache specified
*
* @param \Memcache|\Memcached $cache
* @param MemCacheServer[] $servers
* @throws InvalidConfigException
*/
protected function addServers($cache, $servers)
{
if (empty($servers)) {
$servers = [new MemCacheServer([
'host' => '127.0.0.1',
'port' => 11211,
])];
} else {
foreach ($servers as $server) {
if ($server->host === null) {
throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
}
}
}
if ($this->useMemcached) {
$this->addMemcachedServers($cache, $servers);
} else {
$this->addMemcacheServers($cache, $servers);
}
}

/**
* Add servers to the server pool of the cache specified
* Used for memcached PECL extension.
*
* @param \Memcached $cache
* @param MemCacheServer[] $servers
*/
protected function addMemcachedServers($cache, $servers)
{
$existingServers = [];
if ($this->persistentId !== null) {
foreach ($cache->getServerList() as $s) {
$existingServers[$s['host'] . ':' . $s['port']] = true;
}
}
foreach ($servers as $server) {
if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) {
$cache->addServer($server->host, $server->port, $server->weight);
}
}
}

/**
* Add servers to the server pool of the cache specified
* Used for memcache PECL extension.
*
* @param \Memcache $cache
* @param MemCacheServer[] $servers
*/
protected function addMemcacheServers($cache, $servers)
{
$class = new \ReflectionClass($cache);
$paramCount = $class->getMethod('addServer')->getNumberOfParameters();
foreach ($servers as $server) {
// $timeout is used for memcache versions that do not have $timeoutms parameter
$timeout = (int)($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
if ($paramCount === 9) {
$cache->addserver(
$server->host,
$server->port,
$server->persistent,
$server->weight,
$timeout,
$server->retryInterval,
$server->status,
$server->failureCallback,
$server->timeout
);
} else {
$cache->addserver(
$server->host,
$server->port,
$server->persistent,
$server->weight,
$timeout,
$server->retryInterval,
$server->status,
$server->failureCallback
);
}
}
}

/**
* Returns the underlying memcache (or memcached) object.
* @return \Memcache|\Memcached the memcache (or memcached) object used by this cache component.
* @throws InvalidConfigException if memcache or memcached extension is not loaded
*/
public function getMemcache()
{
if ($this->_cache === null) {
$extension = $this->useMemcached ? 'memcached' : 'memcache';
if (!extension_loaded($extension)) {
throw new InvalidConfigException("MemCache requires PHP $extension extension to be loaded.");
}

if ($this->useMemcached) {
$this->_cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached;
if ($this->username !== null || $this->password !== null) {
$this->_cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$this->_cache->setSaslAuthData($this->username, $this->password);
}
if (!empty($this->options)) {
$this->_cache->setOptions($this->options);
}
} else {
$this->_cache = new \Memcache;
}
}

return $this->_cache;
}

/**
* Returns the memcache or memcached server configurations.
* @return MemCacheServer[] list of memcache server configurations.
*/
public function getServers()
{
return $this->_servers;
}

/**
* @param array $config list of memcache or memcached server configurations. Each element must be an array
* with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
* @see http://php.net/manual/en/memcache.addserver.php
* @see http://php.net/manual/en/memcached.addserver.php
*/
public function setServers($config)
{
foreach ($config as $c) {
$this->_servers[] = new MemCacheServer($c);
}
}

/**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired.
*/
protected function getValue($key)
{
return $this->_cache->get($key);
}

/**
* Retrieves multiple values from cache with the specified keys.
* @param array $keys a list of keys identifying the cached values
* @return array a list of cached values indexed by the keys
*/
protected function getValues($keys)
{
return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys);
}

/**
* Stores a value identified by a key in cache.
* This is the implementation of the method declared in the parent class.
*
* @param string $key the key identifying the value to be cached
* @param mixed $value the value to be cached.
* @see [Memcache::set()](http://php.net/manual/en/memcache.set.php)
* @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.
* @return bool true if the value is successfully stored into cache, false otherwise
*/
protected function setValue($key, $value, $duration)
{
// Use UNIX timestamp since it doesn't have any limitation
// @see http://php.net/manual/en/memcache.set.php
// @see http://php.net/manual/en/memcached.expiration.php
$expire = $duration;

return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire);
}

/**
* Stores multiple key-value pairs in cache.
* @param array $data array where key corresponds to cache key while value is the value stored
* @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.
* @return array array of failed keys. Always empty in case of using memcached.
*/
protected function setValues($data, $duration)
{
if ($this->useMemcached) {
// Use UNIX timestamp since it doesn't have any limitation
// @see http://php.net/manual/en/memcache.set.php
// @see http://php.net/manual/en/memcached.expiration.php
$expire = $duration;
$this->_cache->setMulti($data, $expire);

return [];
} else {
return parent::setValues($data, $duration);
}
}

/**
* Stores a value identified by a key into cache if the cache does not contain this key.
* This is the implementation of the method declared in the parent class.
*
* @param string $key the key identifying the value to be cached
* @param mixed $value the value to be cached
* @see [Memcache::set()](http://php.net/manual/en/memcache.set.php)
* @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.
* @return bool true if the value is successfully stored into cache, false otherwise
*/
protected function addValue($key, $value, $duration)
{
// Use UNIX timestamp since it doesn't have any limitation
// @see http://php.net/manual/en/memcache.set.php
// @see http://php.net/manual/en/memcached.expiration.php
$expire = $duration;

return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire);
}

/**
* Deletes a value with the specified key from cache
* This is the implementation of the method declared in the parent class.
* @param string $key the key of the value to be deleted
* @return bool if no error happens during deletion
*/
protected function deleteValue($key)
{
return $this->_cache->delete($key, 0);
}

/**
* Deletes all values from cache.
* This is the implementation of the method declared in the parent class.
* @return bool whether the flush operation was successful.
*/
protected function flushValues()
{
return $this->_cache->flush();
}
}

配置缓存时使用以下代码

Yii2 migrate使用

试想一个很简单的场景,在使用Yii2开发时,如果对已经有数据的数据表结构进行编辑的话,需要同步数据结构需要在本地导出一份SQL,放到线上去执行SQL,非常的不方便。
而有了Yii2 migrate工具之后,这个问题简直不是问题。以下对常用的表结构操作进行演示。

关键命令

  • 创建migrate

在coding上部署Yii1.x应用

总的来说,由于没有成熟的资料可以参考,部署过程话费了将近一个小时才成功,现在来分享一下经验。

目录配置

由于Paas禁止了本地写功能,所以,如果不加任何处理的话,Yii会尝试在 protected/runtime 目录下写私有文件,结果是肯定没权限的。

经过查找官方文档发现CApplication有个方法叫setRuntimePath,可以设置运行时目录,那么最重要的一点,这个目录的配置肯定要在入口文件中配置,更改之后的入口文件代码如下:

Yii1.x单点登录

背景

Web迅速发展的今天,往往一个产品拥有很多个子站点,SSO技术显得很重要。Yii作为我常用的框架,发现Yii的SSO配置其实是非常简单的。

代码

在所有站点中直接打开 protected/config/main.php 在 components 中加入以下代码,并且把protected/runtime/state.bin文件复制到各个具体的子站点中就可以实现SSO了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'user' => array(
'identityCookie' => array(
'domain' => '.ddhigh.com',
'path' => '/'
),
// enable cookie-based authentication
'allowAutoLogin' => true,
'stateKeyPrefix' => 'ddhigh',
'loginUrl' => array('user/login')
),
'session' => array(
'cookieParams' => array(
'domain' => '.ddhigh.com',
'lifetime' => 0,
'timeout' => 3600
),
),
'statePersister' => array(
'class' => 'CStatePersister',
'stateFile' => './protected/runtime/state.bin'
),

Yii同一站点配置多个用户角色

Yii是基于组件的PHP MVC框架,yii的用户组件调用很方便,但是如果有遇到一个站点有多种用户的时候,如前台用户,后台用户,就需要增加User组件了。

简单来说,就是新建一个用户类去继承 CWebUser类,比如

WebUser继承CWebUser,配置文件在components中增加

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×