Laravel单元测试踩坑

由于国内特殊的网络环境,在使用Laravel做项目的时,一般会吧vendor目录也放到git里去维护。这就间接导致了在不同平台下由Composer引入的PHPUnit执行时运行的文件不一致。

在Mac上composer update后Win平台就不可用,反之一样。

目前临时解决办法就是完全不用Laravel自带的PHPUnit进行单元测试,在系统上独立安装PHPUnit测试。

官方文档:

https://phpunit.readthedocs.io/zh_CN/latest/installation.html

Laravel api路由支持session

laravel5.5版本,使用api路由。在测试接口过程中发现session无法写入,主要原因在于laravel在定义api路由组时并没有设置支持session相关选项。

打开文件:app\Http\Kernel.php

在api数组中增加:

\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,

如下:

'api' => [
    \App\Http\Middleware\EncryptCookies::class,
    \Illuminate\Session\Middleware\StartSession::class,
    'throttle:60,1',
    'bindings',
],

Laravel阿里大鱼短信扩展包使用

最近在做用户注册相关的功能,需要用到阿里大鱼发送短信,使用的这个扩展包(https://github.com/MissMyCat/aliyun-sms),比较好用。

  • 安装 composer require mrgoon/aliyun-sms dev-master

基于laravel框架的使用方法

  • 加载 在config/app的providers中添加 Mrgoon\AliSms\ServiceProvider::class

同时,可以选择性添加aliases

控制台运行: php artisan vendor:publish

根据新增的aliyunsms.php 文件,在.env文件中添加环境变量:

ALIYUN_SMS_AK=your access key
ALIYUN_SMS_AS=your secret key
ALIYUN_SMS_SIGN_NAME=sign name
  • 使用
$aliSms = new AliSms();
$response = $aliSms->sendSms('phone number', 'SMS_code', ['name'=> 'value in your template']);
//dump($response);

非laravel框架的使用方法

  • 加载方式通过composer,不变
  • 使用样例代码如下:
$config = [
        'access_key' => 'your access key',
        'access_secret' => 'your access secret',
        'sign_name' => 'your sign name',
    ];

    $aliSms = new Mrgoon\AliSms\AliSms();
    $response = $sms->sendSms('phone number', 'tempplate code', ['name'=> 'value in your template'], $config);

laravel 社会化登录简介

socialite作为官方包使用起来非常方便,目前支持大多数的社会化登录网站,比如微信,微博,Github等等。

具体支持哪些网站,参见列表:https://socialiteproviders.github.io/about.html

比如网站需要对接微信web授权登录,使用https://socialiteproviders.github.io/providers/weixin.html即可,文档已经写的比较完善,这里不再赘述。

举个微信实际例子:

<?php

namespace App\Http\Controllers\WeChat;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;


class WeChatController extends Controller
{
    public function oauth(Request $request)
    {
        $url = $request->input('url', ''); // 来源url
        return Socialite::with('weixin')->redirectUrl(url("wechat/auth/callback?url=".urlencode($url)))->redirect();
    }

    public function callback(Request $request)
    {
        $oauthUser = Socialite::with('weixin')->user();
        $request->session()->put('user_wechat', $oauthUser->user);
        return redirect($request->input('url'));
    }
}

在中间件中判断session是否存在,不存在调用oauth接口即可。可以说灰常方便了😀

记一次nginx代理配置

背景:

最近部署一台测试服务器在外网,主要用于微信等三方对接必须外网环境,然而数据库等依赖均在内网。

方案 1

打通外网服务器到内网VPN,所依赖的内网服务全走VPN到内网,由于数据库多条SQL执行,每次执行都要经历一次外网到内网的延时,速度实在无法忍受。

方案 2

由外网服务器做代理,转发到内网,所有依赖在内网中解决。

做了张简单的图

代理流程图

外网nginx配置

server{
    listen 443 ssl;
    server_name test.com;
    # 此处省略ssl的配置
    location / {
        # 内网的IP
        proxy_pass         http://192.168.8.12:81;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}

内网nginx配置

server {
    listen 81;
    server_name  localhost;

    charset utf-8;

    root /var/www/test;

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

配置好nginx还需要配置fastcgi参数,这里只列出改动项,根据自己需求更改。

fastcgi_param  REQUEST_SCHEME     https;
fastcgi_param  HTTPS              on;
fastcgi_param  SERVER_PORT        443;
fastcgi_param  SERVER_NAME        test.com;

php7性能测试扩展php-xhprof-extension初探

最近发现测试环境接口响应巨慢,为了验证是哪里出了问题,遂引入性能测试。由于使用的php7,目前可用的性能测试扩展似乎只有php-xhprof-extension可选。

下载安装:https://tideways.com/profiler/downloads

以下为centos安装方法

echo "[tideways]
name = Tideways
baseurl = https://s3-eu-west-1.amazonaws.com/qafoo-profiler/rpm" > /etc/yum.repos.d/tideways.repo
rpm --import https://s3-eu-west-1.amazonaws.com/qafoo-profiler/packages/EEB5E8F4.gpg
yum makecache --disablerepo=* --enablerepo=tideways
yum install tideways-php tideways-cli tideways-daemon安装

安装好后,重启php-fpm

service php-fpm restart

如报异常:

Module 'tideways' already loaded in Unknown on line 0

可能是tideways重复加载了。

[root@test]# php -i | grep .ini$
 Loaded Configuration File => /etc/php.ini
 /etc/php.d/tideways.ini
 user_ini.filename => .user.ini => .user.ini
 vi /etc/php.d/tideways.ini

注释掉:extension=tideways.so

这时检查下扩展是否已经引用

php -m | grep tide
tideways

模块就做好了。

下面开始使用,官网文档使用是这样的:

<?php

tideways_xhprof_enable();

my_application();

$data = tideways_xhprof_disable();

file_put_contents("/tmp/profile.xhprof", serialize($data));

其实就是两个函数的使用,但是当你使用时会报错,什么错误呢

PHP Fatal error:  Uncaught Error: Call to undefined function tideways_xhprof_disable()

再次确认下tideways扩展有没有装好,可以用phpinfo()看一下。发现已经引入了模块。

这就很奇怪了。

再一番Google后,找到了这个问答的评论部分 https://stackoverflow.com/questions/38103784/how-to-use-tideways-on-own-server

Thank you for this. The projects own documentation contains incorrect commands (i.e. tideways_xhprof_enable() and tideways_xhprof_disable()) which meant all I could ever get was fatal errors. Remove the _xhprof token and it all works 🙂

啥意思呢?

就是把函数tideways_xhprof_enable();中的_xhprof去掉就可以了。

然后本人试了下,改成这样

<?php

tideways_enable();

echo "test";

$data = tideways_disable();

file_put_contents("/tmp/profile.xhprof", serialize($data));

这时就可以了,原来方法名都已经改了😳

运行完后可以读取文件,然后反序列化就可以拿到分析结果了。

类似这样:

<?php

array(
    "main()" => array(
        "wt" => 1000,
        "ct" => 1,
        "cpu" => 400,
    ),
    "main()==>foo" => array(
        "wt" => 500,
        "ct" => 2,
        "cpu" => 200,
    ),
    "foo==>bar" => array(
        "wt" => 200,
        "ct" => 10,
        "cpu" => 100,
    ),
)

关于对数据的处理和展示,后续有空的时候更新。

php微信网页授权demo

最近在做微信网页授权相关功能,以下整理简单的授权demo,在开始做之前认真看下微信的开发文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 讲解的十分详细。

没有公共账号的话可以到这里申请一个测试账号: https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

以下代码auth()方法为入口,路由上直接绑定这个方法即可,如果是Laravel建议使用:https://www.easywechat.com/docs/master/zh-CN/official-account/oauth

<?php

namespace App\Http\Controllers\WeChat;

class WeChatController
{
    private $app_id = 'wx107********'; // 替换为自己的app_id
    private $app_secret = '7d29151e****'; // 替换为自己的app_secret


    public function auth()
    {
        $redirect_uri = "https://mango/wechat/auth"; // 请替换成自己的url
        if (!isset($_GET['code'])) {
            $authorizationUrl = $this->get_authorize_url($redirect_uri, "test");
			header('Location: ' . $authorizationUrl);
            exit;
            
        } else {
            $accessToken = $this->get_access_token($this->app_id, $this->app_secret, $_GET['code']);
            var_dump($accessToken);
            if (!empty($accessToken)) {
                $userInfo = $this->get_user_info($accessToken['access_token'], $accessToken['openid']);
                var_dump($userInfo);
            }

        }

    }


    /**

    获取微信授权链接

    @param string $redirect_uri 跳转地址

    @param mixed $state 参数
     */

    public function get_authorize_url($redirect_uri = '', $state = '')
    {

        $redirect_uri = urlencode($redirect_uri);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->app_id}&redirect_uri={$redirect_uri}&response_type=code&scope=snsapi_userinfo&state={$state}#wechat_redirect";
    }


    /**

    获取授权token

    @param string $code 通过get_authorize_url获取到的code
     */

    public function get_access_token($app_id = '', $app_secret = '', $code = '')
    {

        $token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->app_id}&secret={$this->app_secret}&code={$code}&grant_type=authorization_code";
        $token_data = $this->http($token_url, "GET");

        if($token_data[0] == 200)
        {
            return json_decode($token_data[1], TRUE);
        }

        return FALSE;
    }

    /**

    获取授权后的微信用户信息

    @param string $access_token

    @param string $open_id
     */

    public function get_user_info($access_token = '', $open_id = '')
    {

        if($access_token && $open_id)
        {
            $info_url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$open_id}&lang=zh_CN";
            $info_data = $this->http($info_url, 'GET');

            if($info_data[0] == 200)
            {
                return json_decode($info_data[1], TRUE);
            }
        }

        return FALSE;
    }

    public function http($url, $method, $postfields = null, $headers = array(), $debug = false)
    {

        $ci = curl_init();
        /* Curl settings */
        curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($ci, CURLOPT_TIMEOUT, 30);
        curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);

        switch ($method) {
            case 'POST':
                curl_setopt($ci, CURLOPT_POST, true);
                if (!empty($postfields)) {
                    curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
                    $this->postdata = $postfields;
                }
                break;
        }
        curl_setopt($ci, CURLOPT_URL, $url);
        curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ci, CURLINFO_HEADER_OUT, true);

        $response = curl_exec($ci);
        $http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);

        if ($debug) {
            echo "=====post data======\r\n";
            var_dump($postfields);

            echo '=====info=====' . "\r\n";
            print_r(curl_getinfo($ci));

            echo '=====$response=====' . "\r\n";
            print_r($response);
        }
        curl_close($ci);
        return array($http_code, $response);
    }
}

CAS流程图

Laravel使用phpCAS注意点

项目使用框架为Laravel5.5,CAS客户端为https://github.com/apereo/phpCAS
注意事项:
  1. php扩展需要安装xml扩展
  2. phpCAS支持composer引入,composer require jasig/phpcas 注意命名空间为根目录
  3. phpCAS使用的session为原生php session(laravel是自己实现的session机制),需要配置session文件目录读写权限 php-fpm www.conf配置路径
  4. phpCAS错误日志默认写在环境变量$_ENV指定的TMP等路径中,参见https://github.com/apereo/phpCAS/blob/master/source/CAS.php第248行,如果系统是win,则会放在根目录下。
具体使用可以参考phpCAS的docs/examples
附上一张CAS流程图:
CAS流程图