PHPStorm在windows上优雅使用gitbash

gitbash是windows上开发的一个神器终端工具,在执行一些类Linux命令时得心应手。

phpstorm的内置terminal是windows内置的cmd,不适合php开发,于是需要把cmd换成gitbash

直接替换后,会弹出新窗口,需要配置参数,如下面

"C:\git\bin\bash.exe" --login -i

PHP7.4.0一些新特性

2019年11月28日,php.net发布了php7.4.0,根据php.net网站公开信息,博主来翻译一下本次更新的一些特性

强类型的类变量(Typed properties)

类属性在定义的时候,可以设置它的类型

class User {
    public int $id;
    public string $name;
}

上面的例子可以强制$user->id 指定为 integer 类型,$user->name 指定为 string 类型

箭头函数(Arrow functions)

箭头函数提供了一个简练的返回值的语法

$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3, 4]);
// $nums = array(10, 20, 30, 40);

PS: => 右边仅仅支持一句话语句,不支持多个分号的语句

子类可以更改父类方法返回类型(Limited return type covariance and argument type contravariance)

class A {}
class B extends A {}

class Producer {
    public function method(): A {}
}
class ChildProducer extends Producer {
    public function method(): B {}
}

仅仅使用自动加载的时候,才支持此特性。在单个文件中,只有非循环类型引用是可用的,因为所有类在被使用之前引入到工作空间

空合并赋值运算符(Null coalescing assignment operator)

$array['key'] ??= computeDefault();
// 约等于下面的语句
if (!isset($array['key'])) {
    $array['key'] = computeDefault();
}

优雅合并数组(Unpacking inside arrays)

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// $fruits = ['banana', 'orange', 'apple', 'pear', 'watermelon'];
// 等于下面的操作,保留了数组元素的顺序
$fruits = array_merage(['banana', 'orange'], $parts, ['watermelon']);

数字类型文字分隔符(Numeric literal separator)

6.674_083e-11; // float 浮点型
299_792_458;   // decimal 十进制
0xCAFE_F00D;   // hexadecimal 十六进制
0b0101_1111;   // binary 二进制

数字文本可以在数字之间包含下划线。这点博主有点懵逼,等博主用7.4.0跑一下看看什么结果

弱引用(Weak references)

弱引用允许码农保留对某个对象的引用,该对象不会阻止该对象被销毁。

允许从__toString()中抛出异常(Allow exceptions from __toString())

现在允许从 __toString() 方法中抛出异常。之前这么做,php会报一个致命错误(fatal error)。字符串转换方法(__toString())中现有的可恢复致命错误已转换为错误异常。换句话说:__toString方法中可以随便抛异常,上层可以捕获这异常并可以进行相应处理,旧版php不能这么做

CURL

libcurl在版本>=7.56.0中,CURLFile 支持 stream wrappers 和空白文件名(plain file names)

过滤器(Filter)

FILTER_VALIDATE_FLOAT 过滤器支持最小范围(min_range)和最大范围(max_range)参数选项, FILTER_VALIDATE_INT也是如此

FFI扩展

FFI是一个新的PHP扩展(extension),它可以简单地调用C语言类库的原生方法、使用原生变量、创建或访问数据结构

GD扩展

添加了IMG_FILTER_SCATTER 图像过滤器常量以对图像应用散射过滤器。

哈希(Hash)

增加了依赖于Castagnoli’s 多项式的CRC32哈希算法。这个CRC32用于存储系统中,例如iSCSI、SCTP、Btrfs、ext4。

多字节字符串(Multibyte String)

增加了mb_str_split()函数,它提供了和str_split()相同的功能,但是操作的是代码点(code point),而不是字节(Bytes)

代码点(code point)是指与一个编码表中的某个字符对应的代码值。UTF-16编码采用不同长度的编码表示所有Unicode代码点,每个16位二进制表示一个代码单元(code unit)。基本字符的范围为[U+0000~U+FFFF],辅助字符,即上面提到的增补字符,其两个代码单元的范围分别为[U+D800~U+DBFF]和[U+DC00~U+DFFF]。这样很容易就能知道一个代码单元是一个基本字符的编码还是一个辅助字符的第一或第二部分。

OPcache

支持预加载代码

正则(Regular Expressions (Perl-Compatible))

preg_replace_callback()preg_replace_callback_array()函数添加了flags参数, 这个参数支持PREG_OFFSET_CAPTUREPREG_UNMATCHED_AS_NULL常量。这会影响传递给回调函数的匹配内容的数组格式。

PDO

用户名和密码现在可以指定为mysql、mssql、sybase、dblib、firebird和oci驱动程序的PDO DSN的一部分。以前,只支持pgsql驱动程序。如果在构造函数和DSN中都指定了用户名/密码,则构造函数优先。

现在可以在SQL查询中转义问号,以避免它们被解释为参数占位符。使用 ?? 向数据库发送单个问号,例如使用PostgreSQL JSON key 存在 (?) 操作符。

PDO_OCI

现在可以使用PDOStatement::getColumnMeta()方法了

PDO_SQLite

PDOStatement::getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT) 可以检查是否是只读状态,而不用修改数据库内容

PDO::errorInfo() and PDOStatement::errorInfo()中,PDO::setAttribute(PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES, true) 启用了SQLite3 扩展结果码(result code)

尚未完成….

Laravel/Lumen漂亮的解决主从库延迟问题

5.5及以上版本:

// 修改config/database.php文件
// 增加sticky配置项为true
'mysql' => [
	'driver' => 'mysql',
	'host' => env('DB_HOST', '127.0.0.1'),
	'port' => env('DB_PORT', '3306'),
	'database' => env('DB_DATABASE', 'forge'),
	'username' => env('DB_USERNAME', 'forge'),
	'password' => env('DB_PASSWORD', ''),
	'unix_socket' => env('DB_SOCKET', ''),
	'charset' => 'utf8mb4',
	'collation' => 'utf8mb4_general_ci',
	'prefix' => '',
	'prefix_indexes' => true,
	'strict' => true,
	'engine' => null,
	'sticky' => true,
],

5.4及以下版本:

app/Providers/AppServiceProvider增加boot方法,监听每条执行的SQL,如果发现DML语句(INSERT,UPDATE,DELETE)时,则清空从库连接的pdo(readPdo),这样由于Laravel底层安全机制,会默认使用主库连接。这样没有使用事务时,从根本上解决执行DML语句后,再执行DQL(SELECT)语句的延迟问题。

// Laravel/Lumen Connection源码,当readPdo为null时,默认使用主库pdo实例。
public function getReadPdo()
{
     if ($this->transactions >= 1) {
            return $this->getPdo();
     }
      return $this->readPdo ?: $this->pdo;
}

Laravel/Lumen 5.1 实现代码

public function boot()
{
    \DB::listen(function ($sql, $bindings, $time, $connection) {
          $sql = ltrim($sql);
          if (stripos($sql, 'insert') === 0
                   || stripos($sql, 'update') === 0
                    || stripos($sql, 'delete') === 0
           ) {
                 //清空从库连接, 自动使用主库连接
                 \DB::connection($connection)->setReadPdo(null);
           }
     });
}

Laravel/Lumen 5.2 实现代码

public function boot()
{
     \DB::listen(function (QueryExecuted $executed) {
                $executed->sql = ltrim($executed->sql);
                if (stripos($executed->sql, 'insert') === 0
                || stripos($executed->sql, 'update') === 0
                || stripos($executed->sql, 'delete') === 0
               ) {
                     \DB::connection($executed->connection)->setReadPdo(null);//清空从连接,会自动使用主连接
            }
       });
}

参考资料: http://blog.sina.com.cn/s/blog_9bbafb790102win1.html

laravel之Eloquent之Relation 枢纽表关联关系hasManyThrough

使用laravel的eloquent写两个表的关联关系是日常操作,hasOne,hasMany,belongsTo

会遇到有枢纽表(中间表)来做多对多关系用的,hasManyThrough这个方法几个参数比较难记下来

现有三个模型User、City、UserCity,对应表users、cities、user_cities

通过UserCity枢纽表关联User和City关系

现在在User表中写上City的关联关系

class User extends Model
{
    public function cities(): Relation
    {
        return $this->hasManyThrough(
            // 参数1 目标表类名
            City::class,
            // 参数2 枢纽表类名
            UserCity::class,
            // 参数3 枢纽表中和当前表关联的字段名
            'user_id',
            // 参数4 枢纽表中和目标表关联的字段名
            'city_id',
            // 参数5 当前表中和枢纽表关联的字段名,一般是主键
            'id',
            // 参数6 目标表中和枢纽表关联的字段名,一般是主键
            'id');
    }
}

prestissimo给composer加速

要求

  • composer >=1.0.0 (includes dev-master)
  • PHP >=5.3, (suggest >=5.5, because curl_share_init)
  • ext-curl

安装

$ composer global require hirak/prestissimo

卸载

$ composer global remove hirak/prestissimo

基准测试效果

288s -> 26s

$ composer create-project laravel/laravel laravel1 --no-progress --profile --prefer-dist

使用postman调试启用csrf验证的laravel框架方法

由于laravel框架默认开启csrf_token验证,鉴于安全性考量,不关闭csrf_token验证,post、put、patch等请求均会验证csrf_token的正确性,故写下调试教程。

当前Postman是 Version 7.9.0 win32 10.0.18362 / x64

创建一个新的postman environment

选择刚刚创建好的environment

自动获取cookie的csrf_token 到postman的全局环境里

pm.environment.set("XSRF-TOKEN",decodeURIComponent(pm.cookies.get("XSRF-TOKEN")))

在请求中,自动设置csrf_token

{{XSRF-TOKEN}}

附Postman官网地址,推荐下载App原生版本的Postman,不推荐Chrome插件版 https://www.getpostman.com/downloads/

ThinkPHP5 框架redis配置

看了半天官方文档 https://www.kancloud.cn/manual/thinkphp5_1/354116

硬是没测试出来怎么写连接到redis配置,于是干搓源码,看到如何写redis配置

/config/cache.php

return [
    // 驱动方式
    'type' => env('CACHE_TYPE', 'File'),
    'path' => '',
    'prefix' => 'app_name_',
    'expire' => 0,
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'password' => env('REDIS_PASSWORD', ''),
];

写个配置示例,作为备忘

php7.4新语法

// A collection of Post objects
$posts = [/* … */];

$ids = array_map(fn($post) => $post->id, $posts);

上面这个代码等同于下面

$ids = array_map(function ($post) {
    return $post->id;
}, $posts);

使用时有几个注意事项:

  • php7.4才开始支持此语法
  • 以fn关键字开始
  • 只允许有一条语句,一般是return 那句话
  • return关键字可以省略
  • 参数类型和返回类型可以声明

声明类型写法如下:

$ids = array_map(fn(Post $post): int => $post->id, $posts);

如果你想返回一个引用类型,可以这么写:

fn&($x) => $x

还有个特性,在匿名函数里面,引入外部变量时,不需要显式use那个变量

$modifier = 5;

array_map(fn($x) => $x * $modifier, $numbers);

以上的代码,$modifier变量在闭包函数中修改值后,在外部的$modifier不会改变其值,但是$this关键字是给例外。

array_map(fn($x) => $x * $this->modifier, $numbers);