Nginx 系列 - 用 GoAccess 实现可视化并实时监控 access 日志

一、安装使用

1. 安装 GoAccess

本例使用源码安装,./configure 如果出错请看问题说明部分:

$ wget https://tar.goaccess.io/goaccess-1.3.tar.gz
$ tar -xzf goaccess-1.3.tar.gz
$ cd goaccess-1.3
$ ./configure --enable-utf8 --enable-geoip=legacy
$ make
# make install

2. 修改 Nginx 配置

nginx.conf 文件中新增以下配置:

location /report.html {
        alias /usr/local/nginx/html/report.html;
}

并重新加载配置文件:

$ /usr/local/nginx/sbin/nginx -s reload

3. 启动 GoAccess

在 nginx 安装目录下,执行 goaccess 命令:

$ cd /usr/local/nginx
$ goaccess ./logs/access.log -o ./html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED
WebSocket server ready to accept new client connections

4. 访问服务

在浏览器中打开http://127.0.0.1/report.html,如出现以下页面则安装配置成功:

goaccess-report

二、问题说明

本处整理了安装 GoAccess 执行 configure 命令报错和解决办法。

1. 如出现以下报错则需安装 GeoIP:

configure: error: 
    *** Missing development files for the GeoIP library

2. 如出现以下报错则需安装 NCurses:

checking for mvaddwstr in -lncursesw... no
configure: error: *** Missing development libraries for ncursesw

3. 解决办法

官方已经给出了各个平台对应依赖包的名字(传送至官网),只需使用对应平台的包管理工具安装即可,如下图:

goaccess-distribution-package.png

如以上两个报错在 centos 下只需安装 geoip-devel 和 ncurses-devel 即可:

$ yum install geoip-devel ncurses-devel

4. 相关资料

GoAccess 官网:https://goaccess.io


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

使用 docker-lnmp 部署 PHP 运行环境

包含软件

  • nginx
  • php 7.1
  • mysql
  • redis
  • mongo

下载 docker-lnmp

docker-lnmp 默认将同级目录映射到 php-fpm 容器的工作目录,在项目的同级目录下载 docker-lnmp:

$ git clone https://github.com/yanlongma/docker-lnmp.git

启动 docker-lnmp

进入 docker-lnmp 目录,启动服务:

$ docker-compose up -d
Creating network "dockerlnmp_default" with the default driver
Creating dockerlnmp_nginx_1   ... done
Creating dockerlnmp_php-fpm_1 ... done
Creating dockerlnmp_redis_1   ... done
Creating dockerlnmp_mongo_1   ... done
Creating dockerlnmp_mysql_1   ... done

启动成功后,在 docker-lnmp 同级目录新建 phpinfo.php 文件,浏览器访问 http://localhost/phpinfo.php,则可看到 phpinfo() 相关信息。

关闭 docker-lnmp

进入 docker-lnmp 目录,关闭服务:

$ docker-compose down

配置虚拟主机

请参考 nignx/conf.d/yii.conf 文件,配置完需重启服务。

手把手教你使用 GitBook

一、简介

GitBook 是一个支持用 MarkDown 编写文档的软件,支持输出 HTML、PDF、eBook 格式文档。作为开发者我们一般会用它来写一些技术和接口文档。

GitBook 官网:https://www.gitbook.com

二、安装

1. 安装 Node.js

GitBook 是基于 Node.js 的命令行工具,需先下载安装 Node.js,检测是否安装成功:

$ node -v
v6.9.1

2. 安装 GitBook

使用 npm 安装 GitBook,命令如下:

$ npm install -g gitbook-cli

检测是否安装成功:

$ gitbook -V
CLI version: 2.3.2
GitBook version: 3.2.3

三、使用

1. 初始化

新建目录 book,在该目录下执行如下命令进行初始化:

$ gitbook init
warn: no summary file in this book 
info: create README.md 
info: create SUMMARY.md 
info: initialization is finished

该命令将会生成 README.mdSUMMARY.md 两个文件。其中 README.md 是对文档的简单介绍,可以用来作为封面,SUMMARY.md 是文档的目录结构。

也可以自己预先定义好 SUMMARY.md 中的目录结构,然后再用 init 命令初始化,程序将会根据我们的目录结构生成目录和文件,目录结构示例:

# Summary

* [Introduction](README.md)
* [Chapter1](chapter1/README.md)
    * [Section1.1](chapter1/section1.1.md)
    * [Section1.2](chapter1/section1.2.md)
* [Chapter2](chapter2/README.md)

2. 生成并预览

book 目录下执行如下命令,将会生成 HTML 文件并提供预览地址:

$ gitbook serve
Live reload server started on port: 35729
Press CTRL+C to quit ...

info: 7 plugins are installed 
info: loading plugin "livereload"... OK 
info: loading plugin "highlight"... OK 
info: loading plugin "search"... OK 
info: loading plugin "lunr"... OK 
info: loading plugin "sharing"... OK 
info: loading plugin "fontsettings"... OK 
info: loading plugin "theme-default"... OK 
info: found 1 pages 
info: found 0 asset files 
info: >> generation finished with success in 1.0s ! 

Starting server ...
Serving book on http://localhost:4000

在浏览器中打开 http://localhost:4000 即可看到初始化的文档。

如果只需生成 HTML 文件执行 build 命令即可,命令如下:

$ gitbook build

如果需要生成 PDF 文件,由于依赖 ebook-convert,需要安装 Calibre,安装配置完成后,执行如下命令即可:

$ gitbook pdf

3. 编辑器

现在支持实时预览的 markdown 编辑器有很多,可以根据自己的喜好选择,此处仅做推荐:

  • 官方编辑器 GitBook Editor
  • PhpStorm(不要问我为什么,我不会告诉你我是 PHP 程序猿)
  • Sublime Text(其实我写 MD 文档都不用预览,所以没给它装插件,龇牙)

四、配置

通过配置 book.json 中的参数,我们可以配置文档的很多信息,比如标题、侧边栏、插件等。
一个简单的 book.json 文件如下:

{
    "title": "GitBook 使用教程",
    "author": "Yanlong Ma <json_vip@163.com>",
    "description": "GitBook 使用教程 - 马燕龙个人博客",
    "generator": "site",
    "links": {
        "sidebar": {
            "马燕龙个人博客": "http://www.mayanlong.com"
        }
    }
}

下面介绍的插件也需要用到该配置。

五、插件

1. 自带插件

GitBook 默认带有五个插件:highlight、search、sharing、fontsettings、livereload。如果要去除自带的插件,可以在插件名称前面加”-“,如下:

"plugins": ["-search"]

2. 命名规范

GitBook 还支持许多插件,可以从 NPM 上搜索 GitBook 的插件,GitBook 推荐插件的命名方式如下,所以可以通过以下两种方式来搜索 GitBook 的插件或者主题:

gitbook-plugin-X: 插件
gitbook-theme-X: 主题

3. 安装插件

下面介绍如何安装 gitbook-plugin-prism 插件,在 NPM 搜索该插件,插件主页链接为 https://www.npmjs.com/package/gitbook-plugin-prism

首先安装该插件,命令如下:

$ npm i gitbook-plugin-prism

然后在 book.json 中添加该插件,同时禁用默认的 highlight,配置如下:

{
  "plugins": ["prism", "-highlight"]
}

可以根据自己的喜好,配置不同的主题:

"pluginsConfig": {
    "prism": {
        "css": [
            "prismjs/themes/prism-solarizedlight.css"
        ]
    }
}

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

手把手教你发布自己的 Composer 包

一、前言

Composer 是 PHP 用来管理依赖(dependency)关系的工具。我们不仅要学会使用别人提供的包,更要学会制作和分享自己的软件包,下面演示如何创建一个自己的 Composer 包。

准备工作:

  1. 注册 Github 账号
  2. 注册 Packagist 账号

二、实践

本案例演示如何创建一个第三方消息推送(极光推送)的包。

1. 创建 Github 仓库

登录 Github,创建仓库 yanlongma/push,并将代码克隆到本地:

$ git clone https://github.com/yanlongma/push.git

2. 创建 Composer 配置文件

进入项目根目录,创建 Composer 配置文件 composer.json,可以使用命令 compser init 创建也可以手动创建,最终文件内容大体如下:

{
    "name": "yanlongma/push",
    "description": "Third party message push",
    "authors": [
        {
            "name": "Yanlong Ma"
        }
    ],
    "license": "MIT",
    "require": {
        "php": ">=5.4"
    },
    "autoload": {
        "psr-4": {
            "YanlongMa\\Push\\": "src/"
        }
    }
}

3. 提交代码到 Github

根据自己需要实现的功能编写代码,本项目最终项目结构如下:

.git/  
.gitignore  
composer.json  
README.md  
src/
    Client.php    
    JPush.php

代码编写完成且测试没问题后提交代码到 Github。

4. 发布包到 Packagist

登录 Packagist,检出 https://github.com/YanlongMa/push.git 仓库的代码,系统会根据仓库中 composer.json 文件自动设置包的相关信息。

5. 设置 Packagist 中的包自动更新

如果不设置自动同步,每次 Github 中的代码更新,需要在对应包中手动更新,所以建议设置自动更新。步骤如下:

  1. 进入 yanlongma/push 仓库,选择 "Settings -> Integrations & services";
  2. 点击 "Add service",选择 “Packagist”;
  3. 填写你的 Packagist 账号对应的信息(登录后点击查看https://packagist.org/profile/
  4. 配置完成后,点击右上角的“Test service”,如果出现 “Okay, the test payload is on its way.”,则说明配置成功。

6. 使用共享包

发布包到 Packagist 后,根据包名就可以搜索和使用该包了,在自己的项目中申明该包依赖:

$ composer require yanlongma/push

该包的具体使用可以查看 https://github.com/yanlongma/push

  • 发布包到 Packagist 后,可能过几分钟才能在客户端 search 到;
  • 没有打 tag 的要指定 dev,完整命令 composer require "yanlongma/push @dev"

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

PHP 基础篇 - PHP 中 DES 加解密详解

一、简介

DES 是对称性加密里面常见一种,全称为 Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法。密钥长度是64位(bit),超过位数密钥被忽略。所谓对称性加密即加密和解密密钥相同,对称性加密一般会按照固定长度,把待加密字符串分成块,不足一整块或者刚好最后有特殊填充字符。

跨语言做 DES 加密解密经常会出现问题,往往是填充方式不对、编码不一致或者加密解密模式没有对应上造成。常见的填充模式有: pkcs5、pkcs7、iso10126、ansix923、zero。加密模式有:DES-ECB、DES-CBC、DES-CTR、DES-OFB、DES-CFB。

作为一个软件开发者,可以通过工具测试 DES 加密解密,这里推荐一个在线工具:http://tool.chacuo.net/cryptdes

二、实现

PHP 提供了 Mcrypt 系列函数来实现 DES 的加解密,但该扩展中的函数陆续被废弃,自 PHP 7.2.0 起,会移到 PECL。

所以本代码用了更通用的 OPENSSL 方式实现 DES 的加解密,具体的实现和使用代码如下:

<?php

/**
 * openssl 实现的 DES 加密类,支持各种 PHP 版本
 */
class DES
{
    /**
     * @var string $method 加解密方法,可通过 openssl_get_cipher_methods() 获得
     */
    protected $method;

    /**
     * @var string $key 加解密的密钥
     */
    protected $key;

    /**
     * @var string $output 输出格式 无、base64、hex
     */
    protected $output;

    /**
     * @var string $iv 加解密的向量
     */
    protected $iv;

    /**
     * @var string $options
     */
    protected $options;

    // output 的类型
    const OUTPUT_NULL = '';
    const OUTPUT_BASE64 = 'base64';
    const OUTPUT_HEX = 'hex';


    /**
     * DES constructor.
     * @param string $key
     * @param string $method
     *      ECB DES-ECB、DES-EDE3 (为 ECB 模式时,$iv 为空即可)
     *      CBC DES-CBC、DES-EDE3-CBC、DESX-CBC
     *      CFB DES-CFB8、DES-EDE3-CFB8
     *      CTR
     *      OFB
     *
     * @param string $output
     *      base64、hex
     *
     * @param string $iv
     * @param int $options
     */
    public function __construct($key, $method = 'DES-ECB', $output = '', $iv = '', $options = OPENSSL_RAW_DATA | OPENSSL_NO_PADDING)
    {
        $this->key = $key;
        $this->method = $method;
        $this->output = $output;
        $this->iv = $iv;
        $this->options = $options;
    }

    /**
     * 加密
     *
     * @param $str
     * @return string
     */
    public function encrypt($str)
    {
        $str = $this->pkcsPadding($str, 8);
        $sign = openssl_encrypt($str, $this->method, $this->key, $this->options, $this->iv);

        if ($this->output == self::OUTPUT_BASE64) {
            $sign = base64_encode($sign);
        } else if ($this->output == self::OUTPUT_HEX) {
            $sign = bin2hex($sign);
        }

        return $sign;
    }

    /**
     * 解密
     *
     * @param $encrypted
     * @return string
     */
    public function decrypt($encrypted)
    {
        if ($this->output == self::OUTPUT_BASE64) {
            $encrypted = base64_decode($encrypted);
        } else if ($this->output == self::OUTPUT_HEX) {
            $encrypted = hex2bin($encrypted);
        }

        $sign = @openssl_decrypt($encrypted, $this->method, $this->key, $this->options, $this->iv);
        $sign = $this->unPkcsPadding($sign);
        $sign = rtrim($sign);
        return $sign;
    }

    /**
     * 填充
     *
     * @param $str
     * @param $blocksize
     * @return string
     */
    private function pkcsPadding($str, $blocksize)
    {
        $pad = $blocksize - (strlen($str) % $blocksize);
        return $str . str_repeat(chr($pad), $pad);
    }

    /**
     * 去填充
     * 
     * @param $str
     * @return string
     */
    private function unPkcsPadding($str)
    {
        $pad = ord($str{strlen($str) - 1});
        if ($pad > strlen($str)) {
            return false;
        }
        return substr($str, 0, -1 * $pad);
    }

}


$key = 'key123456';
$iv = 'iv123456';

// DES CBC 加解密
$des = new DES($key, 'DES-CBC', DES::OUTPUT_BASE64, $iv);
echo $base64Sign = $des->encrypt('Hello DES CBC');
echo "\n";
echo $des->decrypt($base64Sign);
echo "\n";

// DES ECB 加解密
$des = new DES($key, 'DES-ECB', DES::OUTPUT_HEX);
echo $base64Sign = $des->encrypt('Hello DES ECB');
echo "\n";
echo $des->decrypt($base64Sign);

三、相关链接


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

macOS 上安装 PECL

一、简介

PECL(The PHP Extension Community Library)是 PHP 扩展的存储库,为 PHP 所有的扩展提供提供托管和下载服务。

通过 PEAR(PHP Extension and Application Repository)的 Package Manager 的安装管理方式,可以对 PECL 扩展进行下载和安装。

二、安装

官方提供了 PEAR 在各个平台的安装方式,直接看官方文档的请进【传送门】,macOS 平台官方安装翻译如下。

1. 下载 PEAR

使用 curl 命令下载即可:

$ curl -O https://pear.php.net/go-pear.phar

2. 安装 PEAR

使用 sudo 授权进行安装:

$ sudo php -d detect_unicode=0 go-pear.phar

安装过程需要进行简单的配置,如下:

Below is a suggested file layout for your new PEAR installation.  To
change individual locations, type the number in front of the
directory.  Type 'all' to change all of them or simply press Enter to
accept these locations.

 1. Installation base ($prefix)                   : /usr
 2. Temporary directory for processing            : /tmp/pear/install
 3. Temporary directory for downloads             : /tmp/pear/install
 4. Binaries directory                            : /usr/bin
 5. PHP code directory ($php_dir)                 : /usr/share/pear
 6. Documentation directory                       : /usr/docs
 7. Data directory                                : /usr/data
 8. User-modifiable configuration files directory : /usr/cfg
 9. Public Web Files directory                    : /usr/www
10. System manual pages directory                 : /usr/man
11. Tests directory                               : /usr/tests
12. Name of configuration file                    : /private/etc/pear.conf

1-12, 'all' or Enter to continue: 1

输入 1,将安装根目录修改为 /usr/local/pear;
输入 4,将命令安装到 /usr/local/bin 目录;
其它选项默认即可,一路回车。

3. 检测是否安装成功

出现如下结果,则安装成功:

$ pear version
PEAR Version: 1.10.5
PHP Version: 7.1.7
Zend Engine Version: 3.1.0

三、相关连接

PECL 官方地址:http://pecl.php.net/
PEAR 官方地址:http://pear.php.net/


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

Yii2 教程 - yii2-redis 扩展详解

该教程已被合并到《Yii2 权威指南中文版》中!Yiichina 教程地址为《yii2-redis 扩展详解》!

一、简介

yii2-redis 扩展为 Yii2 框架提供了 redis 键值存储支持。包括缓存(Cache)、会话存储处理(Session),并实现了 ActiveRecord 模式,允许您将活动记录存储在 redis 中。

相关链接

二、安装扩展

在 Yii2 项目根目录,执行以下命令安装:

$ composer require yiisoft/yii2-redis

也可以先在 composer.json 文件中声明如下依赖:

"yiisoft/yii2-redis": "~2.0.0"

再执行下面命令安装:

$ composer update

三、基本使用

继续阅读请确保已安装并开启了 redis 服务,安装请参考《Redis 安装》

1. 配置

在组件中添加如下配置:

'components' => [
    'redis' => [
        'class' => 'yii\redis\Connection',
        'hostname' => 'localhost',
        'port' => 6379,
        'database' => 0,
    ],
]  

2. 示例

下面代码演示了 redis 最基本的 string 类型的使用:

// 获取 redis 组件
$redis = Yii::$app->redis;

// 判断 key 为 username 的是否有值,有则打印,没有则赋值
$key = 'username';
if ($val = $redis->get($key);) {
    var_dump($val);
} else {
    $redis->set($key, 'marko');
    $redis->expire($key, 5);
}

这个类中(yii\redis\Connection)提供了操作 redis 所有的数据类型和服务(String、Hash、List、Set、SortedSet、HyperLogLog、GEO、Pub/Sub、Transaction、Script、Connection、Server)所需要的方法,并且和 redis 中的方法同名,如果不清楚可以直接到该类中查看。

四、缓存组件

该扩展中的 yii\redis\Cache 实现了 Yii2 中的缓存相关接口,所以我们也可以用 redis 来存储缓存,且用法和原来一样。

1. 配置

修改组件中 cache 的 class 为 yii\redis\Cache 即可,配置如下:

'components' => [
    'cache' => [
        // 'class' => 'yii\caching\FileCache',
        'class' => 'yii\redis\Cache',
    ],
],

如果没有配置过 redis 组件,需要在 cache 组件下配置 redis 服务相关参数,完整配置如下:

'components' => [
    'cache' => [
        // 'class' => 'yii\caching\FileCache',
        'class' => 'yii\redis\Cache',
        'redis' => [
            'hostname' => 'localhost',
            'port' => 6379,
            'database' => 0,
        ],
    ],
],

2. 示例

下面代码演示了缓存的基本使用:

// 获取 cache 组件
$cache = Yii::$app->cache;

// 判断 key 为 username 的缓存是否存在,有则打印,没有则赋值
$key = 'username';
if ($cache->exists($key)) {
    var_dump($cache->get($key));
} else {
    $cache->set($key, 'marko', 60);
}

使用文件缓存(FileCache)时,缓存是存储在 runtime/cache 目录下;使用 redis 缓存后,缓存将存储在 redis 数据库中,性能将大大提高。

五、会话组件

该扩展中的 yii\redis\Session 实现了 Yii2 中的会话相关接口,所以我们也可以用 redis 来存储会话信息,且用法和原来一样。

1. 配置

修改组件 session 的配置,指定 class 为 yii\redis\Session 即可,配置如下:

'components' => [
    'session' => [
        'name' => 'advanced-frontend',
        'class' => 'yii\redis\Session'
    ],
],

如果没有配置过 redis 组件,需要在 session 组件下配置 redis 服务相关参数,完整配置如下:

'components' => [
    'session' => [
        'name' => 'advanced-frontend',
        'class' => 'yii\redis\Session',
        'redis' => [
            'hostname' => 'localhost',
            'port' => 6379,
            'database' => 0,
        ],
    ],
],

2. 使用

在开发过程中,切记一定不要使用 PHP 原生的 $_SESSION 去操作,而要使用 Yii 提供的 session 组件,获取方式如下:

$session = Yii::$app->session;

六、ActiveRecord

该扩展中的 yii\redis\ActiveRecord 实现了 Yii2 中的 ActiveRecord 相关接口,所以我们可以使用 AR 的方式操作 redis 数据库。关于如何使用 Yii 的 ActiveRecord,请阅读权威指南中有关 ActiveRecord 的基础文档。

定义 redis ActiveRecord 类,我们的模型需要继承 yii\redis\ActiveRecord,并至少实现 attributes() 方法来定义模型的属性。

主键可以通过 yii\redis\ActiveRecord::primaryKey() 定义,如果未指定,则默认为 id。 primaryKey 必须在 attributes() 方法定义的属性中,如果没有指定主键,请确保 id 在属性中。

下面定义一个 Customer 模型来演示:

class Customer extends \yii\redis\ActiveRecord
{
    /**
     * 主键 默认为 id
     *
     * @return array|string[]
     */
    public static function primaryKey()
    {
        return ['id'];
    }

    /**
     * 模型对应记录的属性列表
     *
     * @return array
     */
    public function attributes()
    {
        return ['id', 'name', 'age', 'phone', 'status', 'created_at', 'updated_at'];
    }

    /**
     * 定义和其它模型的关系
     *
     * @return \yii\db\ActiveQueryInterface
     */
    public function getOrders()
    {
         return $this->hasMany(Order::className(), ['customer_id' => 'id']);
    }

}

使用示例:

// 使用 AR 方式新增一条记录
$customer = new Customer();
$customer->name = 'marko';
$customer->age = 18;
$customer->phone = 13888888888;
$customer->status = 1;
$customer->save();
echo $customer->id;

// 使用 AR 查询
$customer = Customer::findOne($customer->id);
$customer = Customer::find()->where(['status' => 1])->all();

redis ActiveRecord 的一般用法与权威指南中数据库的 ActiveRecord 用法非常相似。它们支持相同的接口和方法,除了以下限制:

  • 由于 redis 不支持 sql,查询方法仅限于使用以下方法:where(),limit(),offset(),orderBy() 和 indexBy()。 【 orderBy() 尚未实现:#1305)】
  • 由于 redis 没有表的概念,因此不能通过表定义关联关系,只能通过其它记录来定义关系。

七、直接使用命令

直接使用 redis 连接,就可以使用 redis 提供的很多有用的命令。配置好 redis 后,用以下方式获取 redis 组件:

$redis = Yii::$app->redis;

然后就可以执行命令了,最通用的方法是使用 executeCommand 方法:

$result = $redis->executeCommand('hmset', ['test_collection', 'key1', 'val1', 'key2', 'val2']);

支持的每个命令都有一些快捷方式,可以按照如下方式使用:

$result = $redis->hmset('test_collection', 'key1', 'val1', 'key2', 'val2');

有关可用命令及其参数的列表,请参阅 redis 命令:


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

PHP 基础篇 - PHP 正则官方文档汇总

一、PCRE 正则语法

下面是 PHP 的 PCRE 正则语法(模式语法)相关文档,详情请查阅相关链接:

二、PCRE 模式修饰符

下面列出了当前可用的 PCRE 修饰符,详情请看官方文档

  • i (PCRE_CASELESS)
  • m (PCRE_MULTILINE)
  • s (PCRE_DOTALL)
  • x (PCRE_EXTENDED)
  • e (PREG_REPLACE_EVAL)
  • A (PCRE_ANCHORED)
  • D (PCRE_DOLLAR_ENDONLY)
  • S
  • U (PCRE_UNGREEDY)
  • X (PCRE_EXTRA)
  • J (PCRE_INFO_JCHANGED)
  • u (PCRE_UTF8)

三、PCRE 函数

下面是 PHP 的 PCRE 函数相关文档,详情请查阅相关链接:


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

PHP 基础篇 - PHP 错误级别详解

一、前言

最近经常看到工作 2 年左右的童鞋写的代码也会出现以静态方法的形式调用非静态方法,这是个 Deprecated 级别的语法错误,代码里不应该出现的。对方很郁闷,说:为什么我的环境可以正常运行呢?

二、详解

代码会不会报错,以及你能不能看到报错信息由 PHP 配置中以下两个参数影响,目前线上主流的配置如下(php.ini 文件中):

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off

下面详细介绍这两个参数:

1. error_reporting

首先来说说 PHP 的错误级别,代码会不会报错由 error_reporting 参数决定,PHP 的错误级别种类如下(点击查看中文版):

E_ALL             - All errors and warnings (includes E_STRICT as of PHP 5.4.0)
E_ERROR           - fatal run-time errors
E_RECOVERABLE_ERROR  - almost fatal run-time errors
E_WARNING         - run-time warnings (non-fatal errors)
E_PARSE           - compile-time parse errors
E_NOTICE          - run-time notices (these are warnings which often result
                    from a bug in your code, but it's possible that it was
                    intentional (e.g., using an uninitialized variable and
                    relying on the fact it is automatically initialized to an
                    empty string)
E_STRICT          - run-time notices, enable to have PHP suggest changes
                    to your code which will ensure the best interoperability
                    and forward compatibility of your code
E_CORE_ERROR      - fatal errors that occur during PHP's initial startup
E_CORE_WARNING    - warnings (non-fatal errors) that occur during PHP's
                    initial startup
E_COMPILE_ERROR   - fatal compile-time errors
E_COMPILE_WARNING - compile-time warnings (non-fatal errors)
E_USER_ERROR      - user-generated error message
E_USER_WARNING    - user-generated warning message
E_USER_NOTICE     - user-generated notice message
E_DEPRECATED      - warn about code that will not work in future versions
                    of PHP
E_USER_DEPRECATED - user-generated deprecation warnings 

其中 WARNING、NOTICE、DEPRECATED 等错误级别会抛出一个错误,但不会终止程序运行。

2. display_errors

再来看一下 display_errors 参数,该参数是是否展示错误信息,只有开启才会显示错误信息(值可以为 on|off、true|false、1|0)。

开发环境最好设置为开启,尽早发现问题,但生产环境一定要关闭。(点击查看中文版官方文档

3. 问题演示

下面使用 PHP 的运行时配置,改变 PHP 的 error_reporting 和 display_errors,演示上面的问题。代码如下:

<?php

// error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);  // 该级别下面的代码不会抛出错误
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", "on");

class Foo
{
    public function test()
    {
        echo 'test';
    }
}

Foo::test();

运行该代码会输出 test,但同时会抛出一个 Deprecated 级别的错误。这种情况应该杜绝,规范开发,从基础做起,人人有责!


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

PHP 基础篇 - PHP 的 BC MATH 系列数学函数

一、常见问题

用 PHP 做计算时经常会遇到精度带来的问题,下面来看两个常见的例子:

1. 运算比较

下面表达式输出的结果不是相等

<?php 

echo 2.01 - 0.01 == 2 ? '相等' : '不相等';  // 不相等

2. 类型转换

下面表达式输出的结果不是201(如果想输出你想要的结果,需要先转 string 再转 int):

<?php

$num = intval(2.01 * 100);
var_dump($num);    // int(200)

你也许会觉得很奇怪,然而这并不是 PHP 的 bug,如果想深入了解可以参考鸟哥的两篇文章:

  1. 关于PHP浮点数你应该知道的(All ‘bogus’ about the float in PHP)
  2. PHP浮点数的一个常见问题的解答

二、BC MATH

用 PHP 提供的 BC MATH 系列数学函数可以解决上面的问题。对于任意精度的数学计算, BC MATH 提供了支持用字符串表示的任意大小和精度的数字的二进制计算,最多为2147483647-1(或0x7FFFFFFF-1)。

下面用 BC MATH 提供的函数解决上面的问题。

1. 运算比较

bccomp — 比较两个任意精度的数字:

<?php

$num = bccomp(2.01 - 0.01, 2, 2);   
var_dump($num); // int(0)

注:如果两个数相等返回 0, 左边的数比较右边的数大返回 1, 否则返回-1。

2. 类型转换

bcmul — 2个任意精度数字乘法计算:

<?php

$num = bcmul(2.01, 100, 0);
var_dump($num);    // string(3) "201"
var_dump(intval($num));    // int(201)

注:返回结果为字符串类型

使用 BC MATH 系列数学函数可以让我们减少失误,避免不必要的错误,如需查看详细参数和其它函数的使用,请查阅 PHP 官方文档:http://php.net/manual/zh/book.bc.php


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:http://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma