PHP7.4后 ?? 与 ?:的区别

PHP7.4版本发布后,新增空(NULL)合并运算符??的语法糖,如果变量存在且值不为 NULL, 它就会返回自身的值,否则返回它的第二个操作数。与以前就存在的简写的条件运算符?:有点类似;

??与?:对比


a = 1;b = a ?: 2;//a 为true就返回自身,否者返回第二个操作数
c =a ?? 2;//a不为null就返回自身,否者返回第二个操作数
// dd(b,c);// int(1)  int(1)a = 0;
b =a ?: 2;//a 为true就返回自身,否者返回第二个操作数c = a ?? 2;//a不为null就返回自身,否者返回第二个操作数
// dd(b,c);// int(2)  int(0)

a = null;b = a ?: 2;//a 为true就返回自身,否者返回第二个操作数
c =a ?? 2;//a不为null就返回自身,否者返回第二个操作数
dd(b,$c);// int(2)  int(2)

空合并运算赋值符 ??=

$name = $name ?? 'john'
可以简写成
$name ??= 'john'

Laravel 模型关系相关函数

模型关系:
\Illuminate\Database\Eloquent\Relations\HasOneOrMany.php
主表 HasOneOrMany 子表

getLocalKeyName();//id 获取主表
getForeignKeyName();//inquiry_id
getQualifiedParentKeyName();//主表.id
getQualifiedForeignKeyName();//从表.inquiry_id

\Illuminate\Database\Eloquent\Relations\BelongsTo.php
从表 BelongsTo 主表
getForeignKeyName();//inquiry_id
getOwnerKeyName();//id
getQualifiedOwnerKeyName();//主表.id
getQualifiedForeignKeyName();//从表.inquiry_id

例如
用户拥有手机
手机属于用户
User->phone()->getForeignKeyName();Phone->user()->getOwnerKeyName();

使用反射函数处理httpresponse object

最近同事在对接接口的时候得到了一个Httpresponse Object对象,这玩意嘛,来,大家感受下

object(HttpResponse)#11 (7) {
  ["content":"HttpResponse":private] => string(1047) "HTTP/1.1 200 OK
Server: Tengine
Date: Fri, 22 Jun 2018 09:52:33 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Cache-Control: no-store
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529661153|1529661153;Path=/
X-Ca-Error-Message: OK
X-Ca-Debug-Info: {"TotalLatency":81,"ServiceLatency":55}

{"code":601,"message":"数据不存在","data":null}"
  ["body":"HttpResponse":private] => string(52) "{"code":601,"message":"数据不存在","data":null}"
  ["header":"HttpResponse":private] => string(995) "HTTP/1.1 200 OK
Server: Tengine
Date: Fri, 22 Jun 2018 09:52:33 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Cache-Control: no-store
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529661153|1529661153;Path=/
X-Ca-Error-Message: OK
X-Ca-Debug-Info: {"TotalLatency":81,"ServiceLatency":55}

"
  ["requestId":"HttpResponse":private] => string(36) "CC7C80AD-3CCB-4F54-B14D-3BDA143AC3BE"
  ["errorMessage":"HttpResponse":private] => string(2) "OK"
  ["contentType":"HttpResponse":private] => string(31) "application/json; charset=UTF-8"
  ["httpStatusCode":"HttpResponse":private] => int(200)
}

这个数据块,让人有点摸不着头脑,尝试使用了以下方法都失败告终
1.强制转数组,(array) data
2.json_decode(json_encode(
data),true)
3.直接用$object->属性

最后在万能的度娘帮助下,使用了php的反射函数处理了数据

$ref = new ReflectionClass($response);
        $props = $ref->getProperties();
        $arr = [];


        foreach ($props as $prop) {
            $prop->setAccessible(true);
            $arr[$prop->getName()] = $prop->getValue($response);
            $prop->setAccessible(false);
        }

最后如愿得到了一个数组类型数据

array(7) {
  ["content"] => string(1048) "HTTP/1.1 200 OK
Server: Tengine
Date: Mon, 25 Jun 2018 02:22:15 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 271C3605-1937-4FF4-8D9B-D8E0F530875C
Cache-Control: no-store
X-Ca-Error-Message: OK
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529893335|1529893335;Path=/
X-Ca-Debug-Info: {"TotalLatency":105,"ServiceLatency":56}

{"code":601,"message":"数据不存在","data":null}"
  ["body"] => string(52) "{"code":601,"message":"数据不存在","data":null}"
  ["header"] => string(996) "HTTP/1.1 200 OK
Server: Tengine
Date: Mon, 25 Jun 2018 02:22:15 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 52
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 271C3605-1937-4FF4-8D9B-D8E0F530875C
Cache-Control: no-store
X-Ca-Error-Message: OK
Vary: Accept-Encoding
ufe-result: A2
Pragma: no-cache
Via: 71aae546d43b[web,200]
Set-Cookie: SERVERID=c0cb0a9f805e8a70fc370b48877e9e8e|1529893335|1529893335;Path=/
X-Ca-Debug-Info: {"TotalLatency":105,"ServiceLatency":56}

"
  ["requestId"] => string(36) "271C3605-1937-4FF4-8D9B-D8E0F530875C"
  ["errorMessage"] => string(2) "OK"
  ["contentType"] => string(31) "application/json; charset=UTF-8"
  ["httpStatusCode"] => int(200)
}

laravel 日志无权限写入报错(服务器生成日志无权限)

原因分析是:其他角色写入日志导致权限不可写

例如:定时器执行任务的用户不是项目组用户等

日志文件的权限也可以在每天创建时配置

解决方案:不适用root用户创建定时用户,使用www用户创建定时用户
crontab -u www -e

如果服务器环境为宝塔,且使用的是宝塔面板的定时任务
可以使用 su -c 和 -s 搭配操作,为计划任务指定执行用户,来解决此问题。
如:
su -c "/www/server/php/73/bin/php -q /www/wwwroot/xx/artisan schedule:run 2>&1" -s /bin/sh www

使用docker搭建swoft 2.x环境

最近项目要使用到框架swoft,但是官方文档以及网上的各路教程均无法直接搭建成功,顾折腾了1天后,记录一下搭建过程:
1.首先VM上新建一个centos7.x的系统,参考:https://www.cnblogs.com/toutou/p/vmware_workstation_centos7.html
2.配置xshell连接虚拟机,参考:https://www.cnblogs.com/shireenlee4testing/p/9469650.html
3.安装docket

yum install docker -y
#安装成功后,启动docker
cd /bin
systemctl start docker
#检查状态
docker ps -a
#出现列表即启动成功

4.安装docker-compose
参考:https://blog.csdn.net/pushiqiang/article/details/78682323

sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
#添加可执行权限
sudo chmod +x /usr/local/bin/docker-compose
#测试
docker-compose --version
#出现以下结果代表成功

docker-compose version 1.16.1, build 1719ceb

5.安装git(可跳过)
yum -y install git
6.关闭snlinux防火墙,参考:https://blog.51cto.com/bguncle/957315
7.拉取swoft项目:参考:https://www.cnblogs.com/a609251438/p/12098988.html

cd /home
#创建www目录
mkdir www
cd /www
git clone https://github.com/swoft-cloud/swoft
#如果上面连接无法拉取,使用下面这条
git clone git://github.com/swoft-cloud/swoft

8.修改docker-compose.yml配置文件

#进入项目目录
cd swoft
#先备份一个文件
cp docker-compose.yml docker-compose-back.yml
#编辑文件
vi docker-compose.yml
#在swoft代码块中添加下面三行
command: /bin/bash
tty: true
stdin_open: true
#并且注释restart: always

9.启动容器并进入:

docker-compose up -d

#查看容器id
docker ps -a

#获取到swoft对应的id或者名字,然后进入到容器内
docker exec -it 243c32535da7 /bin/bash
#或者 
docker exec -it swoft-srv /bin/bash

10.进入后到即为项目的根目录,查看.env文件,如果没有就拷贝一份
cp .env.example .env

11.执行composer安装

#先配置阿里云镜像源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

composer install
#安装依赖成功后,测试是否安装成功
php bin/swoft -v
#出现版本信息即为成功


12.启动项目,参考:https://www.thinkphp.cn/extend/1372.html
php bin/swoft ws:start

Laravel使用migrate更新指定数据表

在项目开发中,数据表往往是分开多次建立的,因此后期使用artisan命令
php artisan migrate
往往会报错
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'xxx' already exists
所以我们可以在database下新建目录new,然后把新建并且数据库中还没有对应表的Migration拷贝到该目录下,然后执行下面命令
php artisan migrate --path=database/new
如果执行还是不成功,可以执行
php artisan migrate:refresh --path=database/new

meta加了缓存静态文件不生效

迫于网站首页打开缓慢,在头部加了缓存静态资源文件的代码,然而发现不生效

<meta http-equiv="Cache-Control" content="max-age=7200" />

打开控制台发现缓存状态为:

Cache-Control:no-store,no-cache,must-revalidate;

Pragma: no-cache;

初步怀疑是服务器配置问题,一番度娘后发现是php.ini配置的问题,打开配置文件

#找到
session.cache_limiter
#默认为nocache,修改为none

修改后重载配置,即可生效

参考链接:
1:http://www.zxsdw.com/index.php/archives/926/
2:https://blog.csdn.net/a624193873/article/details/103130043

TP3.2自定义函数过滤单引号防止sql注入

TP3.2自带的I方法使用的是htmlspecialchars过滤,但是默认是不转移单引号的,这就会增加sql注入的风险,因此我们可以按照TP3官方文档,自定义函数,自行过滤

#首先在function文件中添加方法
#\Application\Common\Common\function.php
function htmlentities_custom(str){
    return htmlentities(str, ENT_QUOTES);
}

#然后在配置DEFAULT_FILTER中添加该自定义方法
'DEFAULT_FILTER' =>  'htmlentities_custom',

#添加完后,清除缓存,即可过滤单引号

php 7.2 安装 mcrypt 扩展: mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl

升级 php 7.2 后,使用微信提供的加解密代码时,提示 call to undefined function mcrypt_module_open() ;大脑疯狂运转1秒钟后,得出结论:php 7.2的扩展有变动;查阅相关资料知晓,mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl。还好,安装过程不复杂。

环境:centos 7

1.yum 安装依赖包:

yum install libmcrypt libmcrypt-devel mcrypt mhash

2.在 php 官网下载 mcrypt 包,php 扩展官网

# wget http://pecl.php.net/get/mcrypt-1.0.1.tgz

# tar xf mcrypt-1.0.1.tgz

# cd mcrypt-1.0.1

3.编译安装 mcrypt

进入到php安装目录

# /usr/local/php/bin/phpize

# ./configure –with-php-config=/usr/local/php/bin/php-config && make && make install

4.在php.ini加上扩展即可

extension=mcrypt.so

5.重启 php-fpm

/etc/init.d/php-fpm restart #可以根据实际情况修改,你也可能是/usr/local/php/sbin/php-fpm

composer安装laravel Changed current directory to没反应

根据官方手册执行composer global require “laravel/installer”

显示Changed current directory to C:/Users/Administrator/AppData/Roaming/Composer

然后没反应了,因为去国外的镜像地址下载了。

应该先将composer换成国内镜像

composer config -g repo.packagist composer https://packagist.phpcomposer.com