这应该是做跨境 wordpress 的人经常遇到了。众所周知,用了 Cloudflare 之后,几乎不用再管 https 的问题。wordpress 当然也是这样。
膘叔的简单人生
这应该是做跨境 wordpress 的人经常遇到了。众所周知,用了 Cloudflare 之后,几乎不用再管 https 的问题。wordpress 当然也是这样。
这个标题写出来其实也挺Low的,只是提醒自己一下。有时候真的会忘,之前用Yii用的太多,突然换到Laravel后,很多东西都不记得。
比如表字段比较,Yii的话可能就是table::find()->andWhere(new DbExpression('xxx > xxx'))【不记得写得对不对,不用IDE,现在的框架类都记不清了】。于是换到laravel 的时候我也理所当然的写了 Table::query()->where(DB::raw('xx > yy'));然后发现生成的SQL是 select * from table where xx > yy is null ....我晕。这个is null是什么鬼?
再后面才发现,原来还有whereRaw。。。。只要写Table::query()->whereRaw('xx > yy')。。记一下。有好多类似鬼方法,什么whereHas,whereIn。。。还是Yii方便啊。自动识别数组。。。
黑黑
Yii2框架用了N年了,每次最受不了的就是他需要下载一堆jquery/inputmask/punycode/yii2-pjax等基础的jQuery插件。想想啊,如果你只是拿Yii2来写Rest,这样的东西,有啥用?
而且比较可恨的是,你为了用这个。你还得全局安全fxp的插件,然后,你还得准备一个github的auth token(虽然 在使用的时候,会提示怎么创建),但就是麻烦!
然后在网上找啊找,找啊找,找到这个插件:yidas/yii2-composer-bower-skip,网址为:https://github.com/yidas/yii2-composer-bower-skip,有一些教程,仔细看了一下,原来他就是引用了一个composer的provide。
查看了一下composer的provide的说明:
此程序包提供了一个其他程序包列表。 这对于通用接口非常有用, 一个包可能依赖于一些虚拟的logger包,任何实现这个logger接口的库都可以在provide中列出来。
好吧,意思就是欺骗其他composer组件说,我已经有了,你不用下载了。。。
然而,在yii 2.0.12后,即使你将这个玩意放在 yiisoft/yii2 前,fxp还是会下载bower/* ,所以你还得将fxp删除:composer globao remove fxp/composer-asset-plugin。再试着用composer update -vvv,会看到确实不下载了。不过这带来的另一个问题就是,服务器上的一些其他Yii的项目,因为有可能使用了yii的asset组件,所以fxp还真不能干掉。
最后:我只能暂时放弃了这个方案,不过我还是参考了这个项目,将他的composer中的内容放到了自己的composer.json文件里。毕竟,如果是在一台新的机器上,就不用担心了。因为我已经很久不用Yii AssetManager了。现在都是blade + vue (虽然 用法也比较恶心,但不比Yii自带的assetmanager差)
在yii1的时候,主从数据库的支持没有那么方便,只能写上多个DB的components,然后在AR的getDB中返回相应的db。这样也可以用来对付主从数据库
'db' =>[
'class' => 'yii\db\Connection',
// 配置主服务器
'dsn' => 'dsn for master server',
'username' => 'master',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => 'php_',//默认为空
// 配置从服务器
'slaveConfig' => [
'username' => 'slave',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => 'php_',
'attributes' => [
// use a smaller connection timeout
PDO::ATTR_TIMEOUT => 10,
],
],
];
'db'=>[
//...上面是一些标准配置
'slaves' => [
['dsn' => 'dsn for slave server 1'],
['dsn' => 'dsn for slave server 2'],
['dsn' => 'dsn for slave server 3'],
['dsn' => 'dsn for slave server 4'],
],
]
'db'=>[
// 配置主服务器
'masterConfig' => [
'username' => 'master',
'password' => '',
'attributes' => [
// use a smaller connection timeout
PDO::ATTR_TIMEOUT => 10,
],
],
// 配置主服务器组
'masters' => [
['dsn' => 'dsn for master server 1'],
['dsn' => 'dsn for master server 2'],
],
//other ...slaves
];
google的leveldb越来越被很多人接受。国内的ideawu基于leveldb还写了一个ssdb的前置扩展用来实现了很多功能,比如标准的getset和hget,hset还有zset,zget,也实现了队列。当然pub/sub就没有办法实现了。毕竟它和redis还是有点区别。
基于标准的ssdb的类,写了个小扩展,扩展了Yii的Cache类:
class CSsdbCache extends CCache
{
/**
* @var string hostname to use for connecting to the redis server. Defaults to '127.0.0.1'.
*/
public $hostname = '127.0.0.1';
/**
* @var int the port to use for connecting to the ssdb server. Default port is 8888.
*/
public $port = 8888;
/**
* @var float
*/
public $timeout = 2000;
public $serializer = false;
public $_cache;
protected $_cachekeys = 'ssdb_cachekey';
public function init() {
parent::init();
}
/**
* @return SSDB
*/
public function getSsdbCache() {
if ($this->_cache !== null)
return $this->_cache;
else {
return $this->_cache = new SimpleSSDB($this->hostname, $this->port, $this->timeout);
}
}
public function getkeys() {
return $this->getSsdbCache()->hkeys($this->_cachekeys, "", "", $this->getSsdbCache()->hsize($this->_cachekeys));
}
/**
* 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 string|boolean the value stored in cache, false if the value is not in the cache or expired.
*/
protected function getValue($key) {
return unserialize($this->getSsdbCache()->get($key));
}
/**
* 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 string $value the value to be cached
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
* @return boolean true if the value is successfully stored into cache, false otherwise
*/
protected function setValue($key, $value, $expire) {
$this->getSsdbCache()->hset($this->_cachekeys, $key, 1);
if ($expire > 0) {
//$expire += time();
return $this->getSsdbCache()->setx($key, serialize($value), (int) $expire);
}
else {
return $this->getSsdbCache()->set($key, serialize($value));
}
}
/**
* 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 string $value the value to be cached
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
* @return boolean true if the value is successfully stored into cache, false otherwise
*/
protected function addValue($key, $value, $expire) {
return $this->setValue($key, $value, $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 boolean if no error happens during deletion
*/
protected function deleteValue($key) {
$this->getSsdbCache()->hdel($this->_cachekeys, $key);
return $this->getSsdbCache()->del($key);
}
/**
* @return boolean whether the flush operation was successful.
*/
protected function flushValues() {
$this->getSsdbCache()->multi_del($this->getkeys());
return $this->getSsdbCache()->hclear($this->_cachekeys);
}
}
其实代码很简单,不过由于ssdb默认没有serialize功能,所以在存储之前,得先主动的serialize,然后get的时候unserialize。不然就没有办法存储数组了。
由于ssdb没有flush功能。所以利用hget/hset将所有的key存储下来。flush的时候把hget获取的key读出来删除。然后再清掉这个hget的key
最后还有expire。ssdb里的setx第三个参数。。。居然不是expire,而是ttl。开始的时候,一直都当成expire。结果浪费了很长时间
一转眼,thinkphp已经走过了7个年头,这在开源软件里也算是一个很长的年头了。
7年,结婚是7年之痒,thinkphp从最初的fcs走到现在,中间经历的坎坷自是不必说了。
创业是一件很艰辛的事情,流年坚持将公司办在上海,就是为了能够使得用户产生很大的信任感。如今,这份信任也确实得到了回报。
看看ThinkPHP七周年页面所说的:
七年来,ThinkPHP专注于WEB应用快速开发。
七年来,ThinkPHP超过了300W次下载。
七年来,ThinkPHP经历了7个里程碑版本。
七年来,ThinkPHP为50W个网站提供了底层框架。
先不管有没有水份,至少现在有不少公司的招聘里,已经有公司将熟悉ThinkPHP框架当成了条件之一。这也能够证明上面的内容的真实性和可靠性了。
所以我还是坚持那句话:时间能够证明一切。
Yii在使用CHtmlRadio的时候,如果参数不正确,会隐藏一个form,导致如果radio的值有3个时,永远只能提交第一个和最后一个
太纠结了。仔细看看,确实 是有一个隐藏FORM,导致本来应该是3个radio的button,变成了6个。
找了一下资料,看了一下源码,果然。。。。有一个hidden。
/**
* Generates a radio button for a model attribute.
* If the attribute has input error, the input field's CSS class will
* be appended with {@link errorCss}.
* @param CModel $model the data model
* @param string $attribute the attribute
* @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
* attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
* A special option named 'uncheckValue' is available that can be used to specify
* the value returned when the radio button is not checked. By default, this value is '0'.
* Internally, a hidden field is rendered so that when the radio button is not checked,
* we can still obtain the posted uncheck value.
* If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
* @return string the generated radio button
* @see clientChange
* @see activeInputField
*/
public static function activeRadioButton($model,$attribute,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
if(!isset($htmlOptions['value']))
$htmlOptions['value']=1;
if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
$htmlOptions['checked']='checked';
self::clientChange('click',$htmlOptions);
if(array_key_exists('uncheckValue',$htmlOptions))
{
$uncheck=$htmlOptions['uncheckValue'];
unset($htmlOptions['uncheckValue']);
}
else
$uncheck='0';
$hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
$hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
// add a hidden field so that if the radio button is not selected, it still submits a value
return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions);
}
太纠结了。居然用unCheckValue设置一下才OK:
echo CHtml::radioButton('btn', false, array(
'value'=>'1',
'name'=>'btnname',
'uncheckValue'=>null
));
CHtml::radioButton('btn', false, array(
'value'=>'2',
'name'=>'btnname',
'uncheckValue'=>null
));
//如果是activeForm,就得这么用
echo $form->radioButton($model, 'name', array(
'value'=>1,
'uncheckValue'=>null
));
echo $form->radioButton($model, 'name', array(
'value'=>2,
'uncheckValue'=>null
));
果然纠结。。。NND
Zend 2.0的框架改动与1比实在是太大了,当然抛开Namespace不说,即使是应用和架构,都有了一部分调整。
Zend实在太庞大了。用来开发项目不一定是首选。以前都是当成类库来用的,毕竟他实现了几乎目前所需要用到的一切功能,甚至GData之类的接口都全了,你还能奢求什么?
要赶时髦?openID和oauth也有,要实用?连pdf库都有,发邮件、本地化、命令行,应有尽有,不是类库是什么?
不过2.0就不太一样。我随便看了一个DB类,和以前的操作都有点不太一样了。或许是一下子没有适应过来那种newZend_Db_TableGateway之类的调用方式吧
手册在这里:http://packages.zendframework.com/docs/latest/manual/en
下载在这里:http://packages.zendframework.com/
还有一个相对比较实用的wiki:http://framework.zend.com/wiki/display/ZFDEV2/Home
可以看看。2.0的架构和代码还是值得学习的。
1、yii在linux下面连接mssql时,connectString一般是dblib:xxxxx,而不是mssql,这是因为用freetds的原因
2、用gii生成mssql的数据表结构时,如果表的字段是类似"Post Time"(即,字段中间有空格),则直接会报错
3、当生成好Model时,使用xxx::model()->findAll()时,如果最初的表结构里有允许某个字段为空,即允许isnull的话,在数据库中,该字段无值时是NULL,findAll就会报错:
CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 4004 General SQL Server error: Check messages from the SQL Server [4004] (severity 16) [(null)]. The SQL statement executed was: SELECT TOP 1 * FROM [dbo].[DatabaseLog] [t]
4、无论是mysql还是sqlserver,当在components中设置columnCase值的时候,都会报错,这是因为在 MysqlSchema中都是采用了大小写敏感的方式(如$column['IsIdenfy'])之类的,当大小写敏感后,如果在attributes 中设置了PDO的ATTR_CASE,都会报错
yii的urlmanager可以让项目在访问的时候隐藏index.php,也可以以更优雅的urlrewrite方式来显示,但这一切需要一些配置,在apache上的配置上就相对比较简单,直接参考wordpress的官方配置就完了,但其实很久以来,一直都没有人写过nginx下的配置。大家都是在根据wordpress配置来更改的,比如lnmp项目中,就是:
location / {
if (-f $request_filename/index.html){
rewrite (.*) $1/index.html break;
}
if (-f $request_filename/index.php){
rewrite (.*) $1/index.php;
}
if (!-f $request_filename){
rewrite (.*) /index.php;
}
}
不过,这两天在看官方guide的文档,原来这些问题,官方已经提供方案了:http://yii.neatcn.com/doc/guide/1.1/en/quickstart.apache-nginx-config#nginx
Yii is ready to work with a default Apache web server configuration. The .htaccess files in Yii framework and application folders restrict access to the restricted resources. To hide the bootstrap file (usually index.php) in your URLs you can add mod_rewrite instructons to the .htaccess file in your document root or to the virtual host configuration:
RewriteEngine on # if a directory or a file exists, use it directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # otherwise forward it to index.php RewriteRule . index.php
You can use Yii with Nginx and PHP with FPM SAPI. Here is a sample host configuration. It defines the bootstrap file and makes yii catch all requests to unexisting files, which allows us to have nice-looking URLs.
server { set $host_path "/www/mysite"; access_log /www/mysite/log/access.log main; server_name mysite; root $host_path/htdocs; set $yii_bootstrap "index.php"; charset utf-8; location / { index index.html $yii_bootstrap; try_files $uri $uri/ $yii_bootstrap?$args; } location ~ ^/(protected|framework|themes/\w+/views) { deny all; } #avoid processing of calls to unexisting static files by yii location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { try_files $uri =404; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php { fastcgi_split_path_info ^(.+\.php)(.*)$; #let yii catch the calls to unexising PHP files set $fsn /$yii_bootstrap; if (-f $document_root$fastcgi_script_name){ set $fsn $fastcgi_script_name; } fastcgi_pass 127.0.0.1:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fsn; #PATH_INFO and PATH_TRANSLATED can be omitted, but RFC 3875 specifies them for CGI fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fsn; } location ~ /\.ht { deny all; } }
Using this configuration you can set cgi.fix_pathinfo=0 in php.ini to avoid many unnesessary system stat() calls.