yii2 配置加速 – N个配置文件生成一个配置文件

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';

 

 

centos 下安装 Let’s Encrypt 永久免费 SSL 证书

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 账号登录需要注意的地方 – 生成密码函数generatePasswordHash很慢

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进程堵死了。

如果你的系统,登录注册很频繁,建议重写一下这个函数,不过是否在某些方面存在安全性,这个我说不好,自己取舍一下。

上面是我测试的结果,大家有兴趣可以都测试试试。