yum install -y enca
在文件夹根目录下面创建文件:iconv_shell.sh
里面填写下面的内容:
#!/bin/bash for file in `find ./ -name '*.php'`; do echo "$file" # iconv -f gb2312 -t utf8 -o $file $file enca -L zh_CN -x UTF-8 $file done
执行:
sh iconv_shell.sh
yum install -y enca
在文件夹根目录下面创建文件:iconv_shell.sh
里面填写下面的内容:
#!/bin/bash for file in `find ./ -name '*.php'`; do echo "$file" # iconv -f gb2312 -t utf8 -o $file $file enca -L zh_CN -x UTF-8 $file done
执行:
sh iconv_shell.sh
yii2的配置方式是N个配置文件,最终通过merge函数合并成一个php数组,如果只有几个配置文件,不会占用太多资源,如果像fecshop这类电商系统,有大量的配置文件,因此需要合并N个配置文件成一个配置文件,具体实现如下:
首先新建文件@app/merge_config.php 设置可写权限, 然后新建文件 @app/web/index-merge-config.php,将index.php的内容复制过来,在 代码:$config = yii\helpers\ArrayHelper::merge函数后面,添加下面的代码,这些代码的用处是将$config数组的内容重新写成php代码保存到文件@app/merge_config.php中
$str = '<?php '.PHP_EOL;
$str .= 'return '.PHP_EOL;
function toPhpCode($arr,$i=0){
$i++;
$tb_i = $i*4;
$tb = ($i-1)*4;
$tb1_str = '';
$tb2_str = '';
for($j=0;$j<$tb;$j++){
$tb1_str .= ' ';
}
for($jj=0;$jj<$tb_i;$jj++){
$tb2_str .= ' ';
}
$mystr = '';
if(is_array($arr) && !empty($arr)){
$mystr .= PHP_EOL .$tb1_str.'['.PHP_EOL;
$t_arr = [];
foreach($arr as $k => $v){
$key = '';
$value = '';
if(is_string($k)){
$key = "'".$k."'";
}else{
$key = $k;
}
if(is_array($v) && !empty($v)){
$value = toPhpCode($v,$i);
}else if(is_array($v) && empty($v)){
$value = '[]';
}else{
if(is_string($v)){
$value = "'".$v."'";
}else if(is_bool($v)){
if($v){
$value = 'true';
}else{
$value = 'false';
}
}else{
if(is_null($v)){
$v = "''";
}
$value = $v;
}
}
$t_arr[] = $tb2_str.$key."=>".$value;
}
$mystr .= implode(','.PHP_EOL,$t_arr);
$mystr .= PHP_EOL .$tb1_str.']'. PHP_EOL;
}
return $mystr;
}
$str .= toPhpCode($config);
$str .= ';';
echo 'generate merge config file success';
file_put_contents('../merge_config.php', $str);
echo 'generate merge config file success';
执行这个文件后,就会将$config数组的内容,以php代码的方式写入到merge_config.php,这样就生成了单文件配置。
在index.php中加载单文件配置
$use_merge_config_file = true;
if($use_merge_config_file){
$config = require('../merge_config.php');
}else{
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php'),
# fecshop services config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
# fecshop module config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
# thrid part confing
# common modules and services.
require(__DIR__ . '/../../common/config/fecshop_local.php'),
# appadmin local modules and services.
require(__DIR__ . '/../config/fecshop_local.php')
);
}
这样就完成了。
下面fecshop的代码我粘贴一下:(供参考)
index.php:
<?php
error_reporting(E_ALL || ~E_NOTICE); //除去 E_NOTICE 之外的所有错误信息
ini_set('session.cookie_domain', '.fancyecommerce.com'); //初始化域名,
$http = ($_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
$homeUrl = $http.'://'.$_SERVER['HTTP_HOST'].rtrim(dirname($_SERVER['SCRIPT_NAME']), '\\/');
/**
* fecshop 使用合并配置(config)数组进行加速,true 代表打开。
* 打开配置加速开关前,您需要执行 http://domain/index-merge-config.php 进行生成单文件配置数组。
* 注意:打开后,当您修改了配置,都需要访问一次上面的链接,重新生成单文件配置数组,否则修改的配置不会生效
* 建议:本地开发环境关闭,开发环境如果访问量不大,关闭也行,如果访问量大,建议打开
*
*/
$use_merge_config_file = false;
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
if($use_merge_config_file){
$config = require('../merge_config.php');
}else{
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php'),
# fecshop services config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
# fecshop module config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
# thrid part confing
# common modules and services.
require(__DIR__ . '/../../common/config/fecshop_local.php'),
# appadmin local modules and services.
require(__DIR__ . '/../config/fecshop_local.php')
);
}
$config['homeUrl'] = $homeUrl;
/**
* yii class Map Custom
*
*/
$yiiClassMap = require(__DIR__ . '/../config/YiiClassMap.php');
if(is_array($yiiClassMap) && !empty($yiiClassMap)){
foreach($yiiClassMap as $namespace => $filePath){
Yii::$classMap[$namespace] = $filePath;
}
}
/**
* 添加fecshop的服务 ,Yii::$service , 将services的配置添加到这个对象。
* 使用方法:Yii::$service->cms->article;
* 上面的例子就是获取cms服务的子服务article。
*/
new fecshop\services\Application($config['services']);
unset($config['services']);
$application = new yii\web\Application($config);
$application->run();
index-merge-config.php
<?php
error_reporting(E_ALL ); //除去 E_NOTICE 之外的所有错误信息
ini_set('session.cookie_domain', '.fancyecommerce.com'); //初始化域名,
$http = ($_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php'),
# fecshop services config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
# fecshop module config
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
# thrid part confing
# common modules and services.
require(__DIR__ . '/../../common/config/fecshop_local.php'),
# appadmin local modules and services.
require(__DIR__ . '/../config/fecshop_local.php')
);
$str = '<?php '.PHP_EOL;
$str .= 'return '.PHP_EOL;
function toPhpCode($arr,$i=0){
$i++;
$tb_i = $i*4;
$tb = ($i-1)*4;
$tb1_str = '';
$tb2_str = '';
for($j=0;$j<$tb;$j++){
$tb1_str .= ' ';
}
for($jj=0;$jj<$tb_i;$jj++){
$tb2_str .= ' ';
}
$mystr = '';
if(is_array($arr) && !empty($arr)){
$mystr .= PHP_EOL .$tb1_str.'['.PHP_EOL;
$t_arr = [];
foreach($arr as $k => $v){
$key = '';
$value = '';
if(is_string($k)){
$key = "'".$k."'";
}else{
$key = $k;
}
if(is_array($v) && !empty($v)){
$value = toPhpCode($v,$i);
}else if(is_array($v) && empty($v)){
$value = '[]';
}else{
if(is_string($v)){
$value = "'".$v."'";
}else if(is_bool($v)){
if($v){
$value = 'true';
}else{
$value = 'false';
}
}else{
if(is_null($v)){
$v = "''";
}
$value = $v;
}
}
$t_arr[] = $tb2_str.$key."=>".$value;
}
$mystr .= implode(','.PHP_EOL,$t_arr);
$mystr .= PHP_EOL .$tb1_str.']'. PHP_EOL;
}
return $mystr;
}
$str .= toPhpCode($config);
$str .= ';';
echo 'generate merge config file success';
file_put_contents('../merge_config.php', $str);
echo 'generate merge config file success';
lets encrypt github地址为:https://github.com/certbot/certbot
1.
git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt chmod +x letsencrypt-auto
./letsencrypt-auto certonly --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com
-d参数后面对应的是域名,在执行的过程中,我遭遇了下面的报错:
Total size: 44 M Downloading Packages: Running rpm_check_debug ERROR with rpm_check_debug vs depsolve: libgdbm.so.2()(64bit) is needed by python-libs-2.6.6-66.el6_8.x86_64 ** Found 7 pre-existing rpmdb problem(s), 'yum check' output follows: 4:perl-5.10.1-141.el6_7.1.x86_64 has missing requires of libgdbm.so.2()(64bit) 4:perl-devel-5.10.1-141.el6_7.1.x86_64 has missing requires of gdbm-devel polkit-0.96-5.el6_4.x86_64 has missing requires of libeggdbus-1.so.0()(64bit) 2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16()(64bit) 2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16(libmysqlclient_16)(64bit) 2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of mysql-libs python-libs-2.6.6-52.el6.x86_64 has missing requires of libgdbm.so.2()(64bit) Your transaction was saved, rerun it with: yum load-transaction /tmp/yum_save_tx-2017-04-07-22-1798AqLE.yumtx Could not install OS dependencies. Aborting bootstrap!
centos 6 需要安装 libgdbm.so.2
wget http://mirror.centos.org/centos/6/os/x86_64/Packages/gdbm-1.8.0-39.el6.x86_64.rpm yum localinstall gdbm-1.8.0-39.el6.x86_64.rpm
完成后,重新执行上面的命令行:
./letsencrypt-auto certonly --standalone --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com -d img.appfront.fancyecommerce.com -d img.apphtml5.fancyecommerce.com -d img.fancyecommerce.com
下面的安装就比较顺利了,下面是我的log
[root@iZ942k2d5ezZ certbot-master]# ./letsencrypt-auto certonly --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com /root/.local/share/letsencrypt/lib/python2.6/site-packages/cryptography/__init__.py:26: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6 DeprecationWarning Saving debug log to /var/log/letsencrypt/letsencrypt.log Failed to find executable apachectl in PATH: /usr/local/mysql/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin How would you like to authenticate with the ACME CA? ------------------------------------------------------------------------------- 1: Place files in webroot directory (webroot) 2: Spin up a temporary webserver (standalone) ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2 Obtaining a new certificate Performing the following challenges: tls-sni-01 challenge for fecshop.appfront.fancyecommerce.com tls-sni-01 challenge for fecshop.appfront.es.fancyecommerce.com ------------------------------------------------------------------------------- Could not bind TCP port 443 because it is already in use by another process on this system (such as a web server). Please stop the program in question and then try again. ------------------------------------------------------------------------------- (R)etry/(C)ancel: R ------------------------------------------------------------------------------- Could not bind TCP port 443 because it is already in use by another process on this system (such as a web server). Please stop the program in question and then try again. ------------------------------------------------------------------------------- (R)etry/(C)ancel: R Waiting for verification... Cleaning up challenges Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem. Your cert will expire on 2017-07-06. To obtain a new or tweaked version of this certificate in the future, simply run letsencrypt-auto again. To non-interactively renew *all* of your certificates, run "letsencrypt-auto renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le [root@iZ942k2d5ezZ certbot-master]# ls
在nginx中添加代码:
listen 443 ssl;
ssl on;
ssl_certificate /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
注意上面的文件路径 ,将 fecshop.appfront.fancyecommerce.com 替换成您的文件路径。因为不同的域名生成的文件名字不同,
nginx填写完的代码如下:
server {
listen 80 ;
listen 443 ssl;
ssl on;
ssl_certificate /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server_name fecshop.appfront.fancyecommerce.com fecshop.appfront.es.fancyecommerce.com;
root /www/web/develop/fecshop/appfront/web;
server_tokens off;
include none.conf;
index index.php index.html index.htm;
access_log /www/web_logs/access.log wwwlogs;
error_log /www/web_logs/error.log notice;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
location ~ /sitemap.xml
{
if ($host ~ .*appfront.es.fancyecommerce.com) {
rewrite ^/sitemap\.xml /sitemap_es.xml last;
}
}
location /fr/ {
index index.php;
if (!-e $request_filename){
rewrite . /fr/index.php last;
}
}
location /es/ {
index index.php;
if (!-e $request_filename){
rewrite . /es/index.php last;
}
}
location /cn/ {
index index.php;
if (!-e $request_filename){
rewrite . /cn/index.php last;
}
}
location /de/ {
index index.php;
if (!-e $request_filename){
rewrite . /de/index.php last;
}
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
location /api {
rewrite /api/([a-z][0-9a-z_]+)/?$ /api.php?type=$1;
}
}
重启nginx,就可以访问https了
2自动续签
lets encrypt证书有三个月的到期时间。可以自动renew,步骤如下:
官方资料:https://certbot.eff.org/docs/using.html?highlight=renew#renewing-certificates
改写到期更新时间
vim /etc/letsencrypt/renewal/fecshop.appfront.fancyecommerce.com.conf
fecshop.appfront.fancyecommerce.com.conf是您生成的名字,这个根据您自己域名,不是我的这个名字,打开这个文件,
将第一行的注释去掉,修改为89days
renew_before_expiry = 89 days version = 0.13.0
89days代表,多少天之前算过期,证书过期时间为90天,如果我设置了89,那么第二天就需要renew了,
如果您设置了30天,那么就是2个月后需要renew,如果时间没到,renew会提示失败。
改好文件后,进入letsencrypt 文件夹,执行
./certbot-auto renew --force-renewal --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start"
`–force-renewal`: 代表强制renew
`–pre-hook`: 是nginx关闭的命令,因为renew需要关闭nginx
`–post-hook`: 是nginx开启的命令,搞完证书后,开启nginx
自动续签可以使用crontab来完成,在linux中执行 crontab -e
添加代码:(www/web_logs/letsencry.log 为日志文件,您需要新建这个文件并设置可写)
06 06 * * * /www/web/test/lets/certbot-master/certbot-auto renew --force-renewal --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start" >> /www/web_logs/letsencry.log 2>&1
下面是我的日志:
[root@iZ942k2d5ezZ certbot-master]# ./certbot-auto renew --force-renewal --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start" /root/.local/share/letsencrypt/lib/python2.6/site-packages/cryptography/__init__.py:26: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6 DeprecationWarning Saving debug log to /var/log/letsencrypt/letsencrypt.log ------------------------------------------------------------------------------- Processing /etc/letsencrypt/renewal/fecshop.appfront.fancyecommerce.com.conf ------------------------------------------------------------------------------- Running pre-hook command: /etc/init.d/nginx stop Renewing an existing certificate Performing the following challenges: tls-sni-01 challenge for fecshop.appfront.fancyecommerce.com tls-sni-01 challenge for fecshop.appfront.es.fancyecommerce.com tls-sni-01 challenge for img.appfront.fancyecommerce.com tls-sni-01 challenge for img.apphtml5.fancyecommerce.com tls-sni-01 challenge for img.fancyecommerce.com Waiting for verification... Cleaning up challenges Generating key (2048 bits): /etc/letsencrypt/keys/0003_key-certbot.pem Creating CSR: /etc/letsencrypt/csr/0003_csr-certbot.pem ------------------------------------------------------------------------------- new certificate deployed without reload, fullchain is /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem ------------------------------------------------------------------------------- Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem (success) Running post-hook command: /etc/init.d/nginx start [root@iZ942k2d5ezZ certbot-master]#
3.开启http2
可以更快的加载https,关于http2的资料为:http://www.jianshu.com/p/47d02f10757f
nginx安装的时候需要添加:
./configure --with-http_v2_module
然后在nginx的ssl配置将listen 443 ssl ;改为:
listen 443 ssl http2;
这样就开启http2了,http2可以更快的加载。
http2的资料:
http://www.infoq.com/cn/news/2015/02/https-spdy-http2-comparison/
4.强制使用https,http访问进行跳转
server {
listen 80;
server_name fecshop.appfront.fancyecommerce.com fecshop.appfront.es.fancyecommerce.com;
rewrite ^(.*)$ https://$host$1 permanent;
}
其他参考地址:https://www.v2ex.com/t/310130
Yii2默认的用户组件,在登录用户,都需要先把密码加密,然后去数据查询核对,
密码加密的代码如下:
\Yii::$app->security->generatePasswordHash($password);
Yii2/base/Security.php
public function generatePasswordHash($password, $cost = null)
{
if ($cost === null) {
$cost = $this->passwordHashCost;
}
if (function_exists('password_hash')) {
/** @noinspection PhpUndefinedConstantInspection */
return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
}
$salt = $this->generateSalt($cost);
$hash = crypt($password, $salt);
// strlen() is safe since crypt() returns only ascii
if (!is_string($hash) || strlen($hash) !== 60) {
throw new Exception('Unknown error occurred while generating hash.');
}
return $hash;
}
最后,发现代码耗时卡在 $hash = crypt($password, $salt);
也即是php的crypt函数。在我的linux上面耗费了520ms
现在的系统验证码,有很多包,是可以破的,玩爬虫的,都有付费的机器学习类的破验证码,普通的文字数字验证码,很轻松能破,验证码一破,账号注册登录被爬虫搞起来,很快就把php进程堵死了。
如果你的系统,登录注册很频繁,建议重写一下这个函数,不过是否在某些方面存在安全性,这个我说不好,自己取舍一下。
上面是我测试的结果,大家有兴趣可以都测试试试。
mongodb支持很多种索引,参看官网:https://docs.mongodb.com/manual/indexes/#index-use
创建一个简单索引: db.collection.createIndex({‘name’:1})
这种方式的索引,默认是比较小的,如果值过长就会报错WiredTigerIndex::insert: key too large to index, failing
可以该种hasded的方式 db.collection.createIndex({‘name’:’hashed’}),
如果name是数组格式,而不是字符串,就不能用hashed的索引格式了,这样,我们需要通过另外的方法来解决
https://docs.mongodb.com/manual/reference/parameters/#param.failIndexKeyTooLong
通过设置参数 failIndexKeyTooLong 为false来解决,文档里面有具体的方法,
我的mongodb的启动方式为:
/usr/bin/mongod -f /etc/mongod.conf
我在 /etc/mongod.conf 配置文件中添加下面的配置
setParameter: failIndexKeyTooLong: false
关于mongodb的config的详细可以参看地址:
https://docs.mongodb.com/manual/reference/configuration-options/
通过上面的方式,重启mongodb,解决了我的问题,不报错了
一:安装Fecshop的环境部署:
本文只讲解在Linux下面部署安装Fecshop的步骤,在windows下面不做阐述(需要安装的东西太多,windows下面部署我没有玩过,遇到的问题也不能提供好的解决方式,故而不做阐述),对于Linux开发环境,可以在window下虚拟一个linux,文档参看: 关于vagrant的安装详情参看
本文是在centos 6下面安装环境的详细步骤。
1.更新linux
yum update
2.yum安装基础
yum install ntp vim-enhanced gcc gcc-c++ gcc-g77 flex bison autoconf automake glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel libtool* zlib-devel libxml2-devel libjpeg-devel libpng-devel libtiff-devel fontconfig-devel freetype-devel libXpm-devel gettext-devel curl curl-devel pam-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel nss_ldap openldap-clients openldap-servers gzip make bzip2bzip2-devel pcre-devel wget ncurses-devel cmake make perl
yum -y install gcc automake autoconf libtool make gcc-c++ glibc libmcrypt-devel mhash-devel libxslt-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel lib
yum -y install gcc gcc-c++
yum install libevent* libtool* autoconf* libstd* ncurse* bison* openssl*
1.2 如果在安装 libevent*的时候报错:
libevent2-devel conflicts with libevent-devel-1.4.13-4.el6.x86_64
则使用下面的安装方式
yum install libevent2 libtool* autoconf* libstd* ncurse* bison* openssl*
查看文件是否存在
ls /usr/lib/libncurses.so
如果找不到,则
find / -name ‘libncurses.so’
我找到的文件如下:
/usr/lib64/libncurses.so
增加快捷方式
ln -s /usr/lib64/libncurses.so /usr/lib/libncurses.so
在开始安装之前,需要下载下面的这些软件,你可以通过我的打包一次下载下面,解压到您的/tools文件夹下面使用,所有的软件的下载地址为(百度云盘):
http://pan.baidu.com/s/1kVwRD2Z,进入下载:linux环境安装所需软件.zip
当然您可以自己去相应官网下载相应的软件。
2.开始安装mysql
安装教程为:
在上述安装中,遇到yum安装的地方可以直接忽略掉,因为上面已经安装了。
3.安装php 以及扩展
php有php5和php7,两种版本,下面都给予了安装文档,建议安装php7,性能比较快
3.1 php7的安装
安装php7以及扩展教程为:
3.2 php5的安装
3.2.1 安装php5.4教程为:
在上述安装中,遇到yum安装的地方可以直接忽略掉,因为上面已经安装了。
3.2.2 安装php5.4的扩展
安装教程为:
注意:上面的安装php5.4扩展的教程中,有一个跳转到mongodb的教程,只需要执行里面的步骤1, 2, 3, 4.2 这几个步骤即可。步骤1,2,3是安装mongodb的步骤,步骤4.2是安装mongodb扩展的步骤,不需要安装php-mongo扩展
安装完成后重启php
/etc/init.d/php-fpm restart
5.安装redis
安装教程为:
注意:只需要执行步骤1,2就可以了。
redis关闭命令:/etc/init.d/redis_6379 stop
redis开启命令:/etc/init.d/redis_6379 start
6.安装nginx
安装教程为:
关于nginx的配置,您可以在上面,在百度云盘下载将环境配置里面
https://pan.baidu.com/s/1kVwRD2Z
下载 “环境配置文件.rar”,解压后,将里面的文件覆盖到linux的根目录。这里面也有php的配置,覆盖即可。
然后在 vim /usr/local/nginx/conf/conf.d/default.conf 修改里面的配置即可。
7.php设置快捷方式:
ln -s /usr/local/php/bin/php /usr/bin/php
8.linux一般安装在内部或者远程服务器,当然也可以通过vagrant安装虚拟机的方式,我们开发的时候都不需要用vim编辑,喜欢win下面的编辑器,那么可以通过下面的方式,通过编辑器的ftp功能远程加载文件的方式,教程如下:
9.其他安装推荐:(非必须安装)
对于yii2中用户登录,我们可在user组件中设置session的超时时间,另外我们在session中设置session的超时时间,他们的关系是如何呢?
下面是他们的配置:
session组件的配置:
'session' => [
/**
* use mongodb for session.
*/
/*
'class' => 'yii\mongodb\Session',
'db' => 'mongodb',
'sessionCollection' => 'session',
*/
'class' => 'yii\redis\Session',
'timeout' => 5,
],
User组件的配置:
'user' => [
'class' => 'fecshop\yii\web\User',
'identityClass' => 'fecshop\models\mysqldb\Customer',
# 是否cookie 登录。
/**
* @var boolean whether to enable cookie-based login. Defaults to false.
* Note that this property will be ignored if [[enableSession]] is false.
*/
'enableAutoLogin' => false,
'authTimeout' => 86400,
],
当session的超时时间为5秒,user的enableAutoLogin设置为false(不使用cookie,而使用session的验证登录方式),超时时间为86400秒,通过结果验证发现,5秒后在刷新页面,发现登录状态失效了,需要重新登录
让把session的超时时间设置为86400,user中的authTimeout设置为5秒,通过结果验证发现,5秒后在刷新页面,发现登录状态失效了,需要重新登录。
结果说明:User 组件,在enableAutoLogin设置为false的情况下(不使用cookie,而使用session的验证登录方式),取的是他们的最小的那个值,原因如下:
public function login(IdentityInterface $identity, $duration = 0)
{
if ($this->beforeLogin($identity, false, $duration)) {
$this->switchIdentity($identity, $duration);
....
public function switchIdentity($identity, $duration = 0)
{
.....
if ($identity) {
$session->set($this->idParam, $identity->getId());
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
if ($this->absoluteAuthTimeout !== null) {
$session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout);
}
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
}
}
identity的id保存一个session,相对当前超时时间保存到一个session,绝对超时时间保存一个session,一共三个session,他们的失效时间为session的配置中的失效时间,因此,如果session组件中配置的时间到期,就无法取出来值,
protected function renewAuthStatus()
{
$session = Yii::$app->getSession();
$id = $session->getHasSessionId() || $session->getIsActive() ? $session->get($this->idParam) : null;
if ($id === null) {
$identity = null;
} else {
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
}
上面代码取出来的$id为空,因此session的超时值首先决定一个大范围,
然后user组件在这个范围内设置用户登录状态的超时时间,如果这个值超出session设置的范围,则session的超时值决定登录状态的超时时间。
另外,session的超时时间,默认为相对超时时间,譬如:你设置的为10秒超时,但是你间隔9秒刷新一次,登录状态会一直保持,如果通过配置更改为绝对超时时间,譬如设置绝对绝对超时时间为20秒,那么9秒刷新一次页面,20秒后,登录状态为失效。
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
if ($this->absoluteAuthTimeout !== null) {
$session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout);
}
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
if ($identity !== null && ($this->authTimeout !== null || $this->absoluteAuthTimeout !== null)) {
$expire = $this->authTimeout !== null ? $session->get($this->authTimeoutParam) : null;
$expireAbsolute = $this->absoluteAuthTimeout !== null ? $session->get($this->absoluteAuthTimeoutParam) : null;
if ($expire !== null && $expire < time() || $expireAbsolute !== null && $expireAbsolute < time()) {
$this->logout(false);
} elseif ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
}
通过上面的代码可以看出,绝对超时时间设置后,如果相对时间不失效,绝对时间失效,那么最终的结果是登录状态失效。
如果enableAutoLogin后,如果登录状态判断失效后,可以通过cookie重新恢复session登录状态,如果登录状态没有失效,cookie的失效时间会更新,也就是当前时间+失效时间为cookie的当前失效时间。
最后,需要说明的是,cookie的登录状态的保持的原理为:
if ($this->enableAutoLogin) {
if ($this->getIsGuest()) {
$this->loginByCookie();
} elseif ($this->autoRenewCookie) {
$this->renewIdentityCookie();
}
}
当session失效后,如果cookie开启就会通过cookie获取当前用户信息,最终的登录状态,还是通过session来体现,cookie的作用是在session失效,但是cookie没有实现的情况下起作用,恢复session的登录状态。
本文是在session登录畅通无阻的前提下,首先要先搞好session登录,这个在这里不做叙述,好多session登录的文章。本文叙述cookie登录的原理和使用
1.具体实现:
1.1 cookie登录配置config方面,配置好的代码如下:
'components' => [
'user' => [
'identityClass' => 'fecshop\models\mysqldb\Customer',
'enableAutoLogin' => true,
'authTimeout' => 3600,
],
],
enableAutoLogin设置为true,就会使用cookie登录,如果设置了enableAutoLogin,那么下面的超时时间authTimeout就会无效,因为这个参数是session的超时时间,不过,我们可以在登录的时候,超时时间也从这个配置参数读取,譬如下面的函数参数$duration,可以从这里读取,如果这样,就会有效。
$duration = \Yii::$app->user->authtimeout; \Yii::$app->user->login($this->getCustomer(), $duration);
上面的结论通过代码解释,如下:
public function login(IdentityInterface $identity, $duration = 0)
{
if ($this->beforeLogin($identity, false, $duration)) {
$this->switchIdentity($identity, $duration);
$id = $identity->getId();
$ip = Yii::$app->getRequest()->getUserIP();
if ($this->enableSession) {
$log = "User '$id' logged in from $ip with duration $duration.";
} else {
$log = "User '$id' logged in from $ip. Session not enabled.";
}
Yii::info($log, __METHOD__);
$this->afterLogin($identity, false, $duration);
}
return !$this->getIsGuest();
}
查看 $this->switchIdentity($identity, $duration);
public function switchIdentity($identity, $duration = 0)
{
$this->setIdentity($identity);
if (!$this->enableSession) {
return;
}
/* Ensure any existing identity cookies are removed. */
if ($this->enableAutoLogin) {
$this->removeIdentityCookie();
}
$session = Yii::$app->getSession();
if (!YII_ENV_TEST) {
$session->regenerateID(true);
}
$session->remove($this->idParam);
$session->remove($this->authTimeoutParam);
if ($identity) {
$session->set($this->idParam, $identity->getId());
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
if ($this->absoluteAuthTimeout !== null) {
$session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout);
}
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
}
}
查看:$this->sendIdentityCookie($identity, $duration);
protected function sendIdentityCookie($identity, $duration)
{
$cookie = new Cookie($this->identityCookie);
$cookie->value = json_encode([
$identity->getId(),
$identity->getAuthKey(),
$duration,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$cookie->expire = time() + $duration;
Yii::$app->getResponse()->getCookies()->add($cookie);
}
通过一层层的函数可以看到,cookie的超时时间是从login()函数中读取出来的,
而不是从\yii\web\User的authTimeout变量读取。最后传递给sendIdentityCookie($identity, $duration)方法,进而设置cookie的超时时间。
另外配置中开启了enableAutoLogin,但是在调用login方法的时候没有设置超时时间变量$duration,同样不会设置cookie,进而配置 enableAutoLogin 无效,下面是代码解释:
public function switchIdentity($identity, $duration = 0){ 函数中的代码
if ($duration > 0 && $this->enableAutoLogin) {
$this->sendIdentityCookie($identity, $duration);
}
}
可以看到,如果超时时间为0,那么不会执行设置cookie的方法
public function sendIdentityCookie($identity, $duration); 进而不会设置cookie
1.2 代码改进:
参考代码:
public function getCustomer(){
if($this->_customer === null){
$this->_customer = Customer::findByEmail($this->email);
}
return $this->_customer;
}
public function login($duration = 86400)
{
if ($this->validate()) {
//return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
return \Yii::$app->user->login($this->getCustomer(), $duration);
} else {
return false;
}
}
在上面的代码,默认cookie的过期时间为86400秒,这样默认就不会cookie超时
如果我我调用:
$model = new CustomerLogin; $model->email = $data['email']; $model->password = $data['password']; $loginStatus = $model->login(0);
由于过期时间填写为0,因此,即使在user组件中开启配置enableAutoLogin=true,
cookie也不会生效。
另外,我如果从\yii\web\User的authTimeout变量读取。来设置cookie的超时时间,也是一个不错的选择,代码如下:
public function login($duration = 0)
{
if(!$duration){
if(Yii::$app->user->authTimeout){
$duration = Yii::$app->user->authTimeout;
}
}
if ($this->validate()) {
//return \Yii::$app->user->login($this->getAdminUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
return \Yii::$app->user->login($this->getCustomer(), $duration);
} else {
return false;
}
}
2. cookie超时时间刷新。
在默认的情况下,如果登录成功账户,每次请求访问Yii::$app->user->identity,都会刷新cookie的超时时间,设置自动更新的变量为:
public $autoRenewCookie = true;
该变量默认为true,所以不需要在配置中设置这个变量,使用默认就好。
cookie超时时间刷新的原理解释,下面是详细代码:
执行Yii::$app->user->identity,对应的是下面的函数
public function getIdentity($autoRenew = true)
{
if ($this->_identity === false) {
if ($this->enableSession && $autoRenew) {
$this->_identity = null;
$this->renewAuthStatus();
} else {
return null;
}
}
return $this->_identity;
}
会执行renewAuthStatus()方法
protected function renewAuthStatus()
{
$session = Yii::$app->getSession();
$id = $session->getHasSessionId() || $session->getIsActive() ? $session->get($this->idParam) : null;
if ($id === null) {
$identity = null;
} else {
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
}
$this->setIdentity($identity);
if ($identity !== null && ($this->authTimeout !== null || $this->absoluteAuthTimeout !== null)) {
$expire = $this->authTimeout !== null ? $session->get($this->authTimeoutParam) : null;
$expireAbsolute = $this->absoluteAuthTimeout !== null ? $session->get($this->absoluteAuthTimeoutParam) : null;
if ($expire !== null && $expire < time() || $expireAbsolute !== null && $expireAbsolute < time()) {
$this->logout(false);
} elseif ($this->authTimeout !== null) {
$session->set($this->authTimeoutParam, time() + $this->authTimeout);
}
}
if ($this->enableAutoLogin) {
if ($this->getIsGuest()) {
$this->loginByCookie();
} elseif ($this->autoRenewCookie) {
$this->renewIdentityCookie();
}
}
}
如果enableAutoLogin开启,如果登录就会执行renewIdentityCookie方法。
protected function renewIdentityCookie()
{
$name = $this->identityCookie['name'];
$value = Yii::$app->getRequest()->getCookies()->getValue($name);
if ($value !== null) {
$data = json_decode($value, true);
if (is_array($data) && isset($data[2])) {
$cookie = new Cookie($this->identityCookie);
$cookie->value = $value;
$cookie->expire = time() + (int) $data[2];
Yii::$app->getResponse()->getCookies()->add($cookie);
}
}
}
该方法会重新设置超时时间,通过上面的几个函数知道了原理,那么问题来了,如果我登录了用户,超时时间设置的为10秒,我开始浏览器文章或者干其他的,每次访问间隔3秒,如果这些页面都没有执下面的行代码:
Yii::$app->user->identity
那么,10秒后,用户登录状态就会被超时,需要重新登录,(有一些间接的方法也会执行上面的代码,譬如Yii::$app->user->isGuest ,就会间接调用执行Yii::$app->user->identity,这种情况下不会超时)。
public function getIsGuest()
{
return $this->getIdentity() === null;
}
因此,如果登录状态要持久下去,那么为了保持登录状态,每个页面请求后,都需要执行Yii::$app->user->identity,然后cookie的超时时间就会更新,这样3秒请求一次,登录状态会一直保持下去。
上面是yii2 cookie使用过程中要注意的一些问题。总体来说还是不错,但在实际过程中,还需要结合一下其他,譬如cart 和customer login都使用cookie,超时时间要一致,那么,cart和customer的cookie超时时间要一致,并且,在每次调用Yii::$app->user->identity,都必须执行更新cart的cookie超时时间,因此,需要重写Yii\web\User。
下一个章节介绍实现的具体步骤。
console 是命令行操作,对于frontend和backend是基于web的操作,最终的结果是一样的,都是传递数据给php,下面做一个例子:
1.安装frontend ,然后执行init,可以参看这里:
2.新建文件夹/console/controllers/script,然后新建文件TestController.php
3.编辑文件内容:
<?php
namespace console\controllers\script;
use Yii;
use yii\console\Controller;
use fec\helpers\CDate;
use fec\helpers\CConfig;
use appadmin\code\Website\models\WebsiteBaseInfo;
class TestController extends Controller
{
public $_mongodbdb;
public function actionGetbegindate(){
echo '2015-05-20';
}
public function actionCreatecollindexer(){
echo 'create Coll index success';
}
public function actionMy($param1,$param2=''){
echo "param1:".$param1;
echo "param2:".$param2;
}
}
4.回到yii advanced的根目录执行:
[root@services datacenter_1000]# ./yii script/test/my 111 2222 param1:111param2:2222 [root@services datacenter_1000]#
解读:
script/test/my 这个和web一样,对应到上面创建的TestController的 actionMy()方法,111为传递的第一个参数,222给传递的第二个参数。
到这里,基本就一个helloworld就写好了,其他的和web没有什么两样。
5.在模块中使用console:(下面是一个在模块TA中使用console的例子)
'modules'=>[
'ta' => [
'class' => 'appadmin\code\Ta\Module',
'params'=> [
'channel_type' => [
'ppc' => 'PPC类型',
],
],
],
]
在ta模块的Module中添加代码:
<?php
namespace appadmin\code\Ta;
use Yii;
class Module extends \fec\AdminModule
{
public function init()
{
//echo 1;exit;
# 以下代码必须指定
# web controller
if (Yii::$app instanceof \yii\web\Application) {
$this->controllerNamespace = __NAMESPACE__ . '\\controllers';
# console controller
} elseif (Yii::$app instanceof \yii\console\Application) {
$this->controllerNamespace = __NAMESPACE__ . '\\console';
}
parent::init();
}
}
通过上面判断是console的application还是web的application,取执行相应的controller
譬如我写的一个导入excel的脚本(在ta模块中):
<?php
namespace appadmin\code\Ta\console\import;
use Yii;
use appadmin\code\Ta\models\WebsiteChannel;
use yii\console\Controller;
use fec\helpers\CExcel;
class ExcelController extends Controller
{
public function actionChannel(){
$channel_excel_file = Yii::getAlias('@appta/var/import/channel.xlsx');
$arr = CExcel::getExcelContent($channel_excel_file);
//var_dump($arr);
$i = 0;
foreach($arr as $one){
$i++;
if($i == 1){
continue;
}
$channel = trim($one['A']);
$channel_child = trim($one['B']);
$domain = trim($one['C']);
$one = WebsiteChannel::find()->where([
'channel' => $channel,
'channel_child' => $channel_child,
'domain' => $domain,
])->one();
if(!$one['id']){
$WebsiteChannel = new WebsiteChannel;
}else{
continue;
}
//SEO>SEM>KOL>SNS
$WebsiteChannel->channel = $channel;
$WebsiteChannel->channel_child = $channel_child;
$WebsiteChannel->domain = $domain;
$WebsiteChannel->created_person = 'program_script';
$WebsiteChannel->save();
unset($WebsiteChannel);
}
}
}
我新建一个shell文件
appta/shell/import/channel.sh,内容为:
#!/bin/sh DIR=$(cd `dirname $0`; pwd) $DIR/../../../yii ta/import/excel/channel
然后执行这个shell就会执行上面的模块Ta里面的console部分。
public static function create_index(){
$indexs = [
['cid' => -1],
['market_person' => -1],
['created_at_date' => -1,'market_person' => -1],
];
$options = ['background' => true, 'socketTimeoutMS' => 300000];
foreach($indexs as $columns){
self::getCollection()->createIndex($columns,$options);
}
}
$indexs里面的每一个数组就是一个索引,
$options 里面的background代表后台执行的意思,因为添加索引,如果不加这个选项,如果表很大,要执行几十分钟,那么库会被加锁。