Leo Chen

Leo Chen

Beijing
298 posts
Mysql

关于 MySQL enum 类型的一些测试

背景: 在开发项目时通常会遇到一些状态字段,例如订单的状态有 待支付、已支付、已关闭、已退款 等,我以前做的项目都是把这些状态用数字存在数据库中,然后在 php 代码中用常量来维护一份映射表,例如: const STATUS_PENDING = 0; const STATUS_PAID = 1; const STATUS_CLOSED = 2; const STATUS_REFUNDED = 3; 但是在实际使用过程中发现并不是那么好用,由于各种原因(追查 bug、临时的统计需求等)我们常常需要登录到 mysql 服务器里手动执行一些 sql 查询,由于许多表都有状态字段,写 sql 时必须对照的 php

  • Leo Chen
    Leo Chen
virtualbox

如果你用了 Homestead 并且升级到 macOS High Sierra,你可能需要点进来

昨晚我尝试在一个本地项目里执行 php artisan migrate:fresh,结果提示 Base table or view not found: 1146 Table 'orders' doesn't exist (SQL: alter table orders add refund_no varchar(255) null after ship_status) 我确认 migrations 目录下有 create_orders_table 这个文件,并且给 orders 添加字段的 migration 文件的生成时间是晚于 create_orders_

  • Leo Chen
    Leo Chen

自制 PPA 国内加速镜像

最近把服务器系统从 CentOS 更改为 Ubuntu,Ubuntu 系统本身的源可以用阿里云或者 tuna 的镜像,速度很快很稳定。但是搜了一圈却没有发现有 ppa 的镜像,更别说国内的镜像了。ppa 上有许多官方仓库中没有的包,或者官方仓库并不提供某些包的最新版本,但可以在 ppa 上找到,比如官方源里的 php 版本目前是 7.0,而 ppa 上的是 7.1。 在 google 的搜索结果中发现了一份 gist 代码,功能就是制作一个 ppa 镜像,简单看了下代码,发现是使用 wget 命令直接递归下载 ppa 服务器上的文件,这说明

  • Leo Chen
    Leo Chen

写了个小轮子:新版 QQ 企业邮箱 SDK

最近在重构公司内部的员工系统,员工入职离职需要与 QQ 企业邮箱联动,看了一眼 packagist 上面已有的轮子,都是使用不那么稳定的旧版接口,所以只好自己造个轮子。 github: https://github.com/leo108/qq-exmail composer require leo108/qq-exmail -vvv 具体使用方法直接看项目 ReadMe,我就不复制粘贴了。 这个项目的核心功能我只花了数个小时就完成了(和写文档花的时间差不多),这得益于我的另外一个项目php_sdk_skeleton,这是一个方便大家造轮子的小工具,目前文档还不完善,大家先别急着踩坑,点个 star 就行。

  • Leo Chen
    Leo Chen

给阿里云 VPC 中的 Ubuntu ECS 配置自定义 DNS 服务器

由于历史原因一直在用经典网络的 ECS,在安全性方面要弱于 VPC,所以决定将服务器迁移到 VPC 中。给 ECS 配置自定义 DNS 服务器的好处是可以给 ECS 分配自定义后缀的域名,比如 web01.abc,这样在内网定位服务器就很方便,不用去记一堆内网 IP,如果配置得当,还可以通过 host $ip 这个命令寻找 IP 对应的服务器。 一开始的时候我按照给经典网络 Centos ECS 配 DNS 服务器的方法来操作,直接修改 /etc/resolv.conf,把里面的 nameserver 配置改成了自己内网中 DNS 服务器 IP,但是发现重启服务器之后会被重置成阿里云分配的

  • Leo Chen
    Leo Chen
API

API 文档神器 Swagger 介绍及在 PHP 项目中使用

Swagger 是我目前用过的最优秀的 Api Doc 协议没有之一。它与其他 Api Doc 协议(如apidocjs)最大的差别在于,Swagger 不仅仅可以定义 Api 的 Route / Request Param 和 Response,还可以定义 Definitions Object / Security Definitions Object 以及 Reference Object。 以一个电商项目为例,系统里有 商品(Product)和 订单(Order)两个 Model,其中 Order 有一个 product_id 字段用于关联对应的商品;

  • Leo Chen
    Leo Chen
cas

Laravel 集成 phpCAS 踩坑记

CAS 是目前比较流行的单点登录协议,官方提供了 php 版本的 client 端 phpCAS,到目前为止其编码风格还一直停留在 PEAR 时代,连命名空间都没有使用。好在 phpCAS 支持 composer 引入,做过几个 Laravel 项目引入也没有什么问题,然而这两天有一个项目需要从单机部署变成多机部署,万万没想到在这里踩了一些坑,在此记录一下。 回调坑 在跳转到 CAS Server 进行认证时发现,传入的回调地址被加上了端口8080。因为是多机部署,所以访问请求会先经过负载均衡器(阿里云 SLB),再到达 web 服务器,而这个8080是 web 服务器的监听端口。 于是追查 phpCAS 生成回调地址的逻辑,发现有这么一段代码: if

  • Leo Chen
    Leo Chen
Laravel

Laravel 技巧之 定时任务

定时任务 Scheduled Tasks 是 Laravel 提供的组件之一,稍微上点规模的项目应该都会用到,比如开发微信应用时通过定时任务去刷新access token,比如每天定时发推送提现用户要记得签到。对于定时任务的基本用法,官网文档已经描述得很详细了,这里不再多说。 本文主要是介绍定时任务在实际应用中的两个小技巧: 1. 多个任务并行执行 先简单介绍一下 Laravel 定时任务组件的基本原理: 当cli初始化完毕之后,系统会调用 App\Console\Kernel::schedule 方法,也就是我们定义定时任务列表的地方,这个方法里每调用一次 $schedule->command() 就会生成一个 Illuminate\Console\Scheduling\Event 对象并保存在 $schedule->events 数组里。当执行 php artisan

  • Leo Chen
    Leo Chen
Laravel

Laravel技巧之Pivot

在关系式数据库中,要定义一个符合范式的多对多表关系需要一个中间表作为两个表的关系。在Laravel中这个表称为pivot,在查询出关联的记录之后,可以通过pivot属性来访问关联表的字段: $user = App\User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } 在实际应用中,这个中间表可能不仅仅包含两个表的外键,还有一些附加的字段,举个例子: 一个用户可以属于多个部门,即用户和部门是多对多关系,一个用户在不同部门里角色可能不一样,即用户和角色也是多对多。这个中间表的结构如下: +---------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra

  • Leo Chen
    Leo Chen
Logstash

Monolog优化及打造ELK友好的日志格式

Monolog是目前最流行的php日志库,许多流行的php框架都用Monolog作为默认的日志库,例如symfony、laravel。 默认情况下,symfony、laravel在打印日志时都是来一条日志就打印一行到日志文件,这样就没有办法按照请求将日志聚合起来,更麻烦的是,多个并发的请求打印的日志是交叉的,这给本地调试、线上排查bug带来了许多麻烦。另外一个附带的问题是每写一行日志都得打开日志文件、写入日志、关闭日志文件,会产生大量的磁盘IO(即使是使用Monolog自带的BufferHandler将当前请求产生的所有日志缓冲到内存,等到请求结束再统一写入日志文件,也是每一条日志都得打开、写入、关闭日志文件)。 我希望的结果是: 一次请求所产生的日志需要一次打开、写入、关闭日志文件。 一次请求所产生的日志是紧密相连的,不会因为高并发而产生日志交叉的情况。 能够通过Logstash的multiline来聚合一次请求所产生的所有日志。 一些访问数据(如访问时间、访问者IP、访问的Url、该请求的执行时间等)能够被Logstash直接解析成字段,并能够在Kibana中查询、筛选。 最终代码如下: namespace App\Extensions\Log; use Carbon\Carbon;

  • Leo Chen
    Leo Chen
Laravel

在js中实现Laravel的route函数

在Laravel的路由模块里,我们可以给每一个路由设定一个名字,比如: Route::get('/blog/{blog}', 'BlogController@show')->name('blog.show') 然后就可以通过 route('blog.show', ['blog' => 1]) 来获取到这个路由的访问地址,后端跳转可以用 return redirect()->route('blog.show', ['blog' => 1]); 这样做的好处是如果发生url变更,比如我想把’/blog/{blog}’改成’/boke/{blog}’,只需要改路由文件,别的地方都不用调整。

  • Leo Chen
    Leo Chen
chrome

Chrome选择上传文件反应慢原因及在OctoberCMS中的解决方案

不知道啥时候起,在有些网站点击选择上传文件反应很慢,要等好一会儿才会出现文件选择框,这两天在测试OctoberCMS时也出现这个问题,实在恼人,就想看看怎么解决。 搜索了一下,发现有人已经给出解决方案:http://blog.csdn.net/xiaolyuh123/article/details/52680505 (后来发现不完全正确) 选择文件时如果设定可选的文件类型为*时,就会出现反应慢的情况,如果指定了具体的文件类型如image/jpg image/gif就没有问题。 于是我追了一下OctoberCMS里的代码,发现OctoberCMS里本身就是指定了具体的类型,具体定义在October\Rain\Filesystem\Definitions这个类的imageExtensions方法里,难道是那个人的结论不对? 于是我做了一个最小系统测试,把类型限定成一个,比如只允许jpg格式的图片,在OctoberCMS里的修改办法是在fields.yml中对应的字段增加一个fileTypes数组,只放一个值jpg。然后刷新页面,点击上传文件时立马出现了文件选择框。 这样就很明白了,肯定是OctoberCMS定义的某个类型会导致Chrome弹出文件选择框慢,一个个尝试了过去,最终发现是svg这个类型导致的。 所以最终的解决方案是在fields.

  • Leo Chen
    Leo Chen
chrome

Chrome 批量删除指定站点历史记录

Chrome地址栏的输入联想会从访问历史里搜索,有时候个别站点已经不再使用,却仍在历史记录里影响地址栏的输入联想,今天打算清理某个站点,于是打开 chrome://history/ ,在顶部的搜索栏输入那个站点的域名,搜完发现Chrome居然没有提供全选的功能,这一百多个单选框一个个点过去还不得累死。 作为一个伪前端,这个还是难不倒我的,Chrome没提供就自己写一个吧。 打开Chrome开发者工具,切换到Console一栏,注意要选择“histroy (history-frame/)”,然后输入代码 for(let x of document.getElementsByClassName('entry')) { x.childNodes[0].childNodes[0].childNodes[0].setAttribute('checked', true); } 回车,即可选中所有的单选框,然后点击“移除所选项”即可。

  • Leo Chen
    Leo Chen
cas

PHP CAS Server/Laravel CAS Server 2.0发布

没想到1.0版本才发布,就直接跳到2.0了。主要是当时在设计核心接口的时候没考虑好,有一些遗漏,调整之后属于不兼容的更新,因此只能跳到下一个大版本了。 修复: 退出登录时页面跳转参数不生效问题。 将令牌字段长度调整为256。 禁用用户之后,用户还可以登录。 新增: 实现CAS协议中的Proxy相关接口和逻辑。

  • Leo Chen
    Leo Chen
cas

simple_cas_server项目废弃,新建两个坑

两个月前挖的坑这么快就弃了= = 是因为当初的没有考虑清楚项目的定位,既想实现CAS协议的服务端逻辑,又想加入用户管理、服务管理等等一堆东西。 而这种类型开源项目的受众极有可能是中小型企业,他们是需要一个CAS的服务端,但用户管理这类的周边需求不尽相同,有的可能还要求集成第三方登录,功能做多做少都不合适,所以决定拆分成两个项目:一个专注于实现CAS服务端逻辑,不包含任何与用户交互的前端代码;另一个依赖于前者,对外提供用户、服务管理的入口,并尽可能的插件化,使用者可以根据自己的情况定制。 新坑1:laravel_cas_server 负责实现CAS协议的逻辑。目前单测已经比较完善,代码覆盖率超过85%,处于可用状态,欢迎Star。 新坑2:php_cas_server 负责提供用户交互,测试用例正在编写中。

  • Leo Chen
    Leo Chen
PHP

关于PSR-6的一些思考

之前想自己造一个缓存的轮子,就去看了一下PSR6的定义,并根据psr/cache提供的接口来实现。 当我在实现CacheItemPoolInterface接口时,对于save方法感到困惑,save方法只接收一个CacheItemInterface类型的参数,但是CacheItemInterface这个接口却没有提供类似getExpireTime的方法,这就导致CacheItemPoolInterface没有办法获取到缓存项的过期时间,也就没办法正确地将数据写入到缓存存储里。 看过几个比较著名的PSR-6实现,例如 symfony/cache php-cache/cache tedious/Stash 然而这些实现版本在实现save方法时并不是非常优雅。 比如symfony/cache public function save(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } if ($this->deferred) { $this->commit(); } $this->deferred[$item->

  • Leo Chen
    Leo Chen
cas

又撸了一个开源项目

CAS Server的一个简单PHP实现版本,支持v1/v2/v3三个版本的CAS协议,但是不支持代理(Proxy)相关的接口。 有一个简单管理后台,可以管理用户(User)和服务(Service)。 地址:https://github.com/leo108/simple_cas_server 做这个事情的起因是,公司内部引入了许多开源系统,例如gitlab、jenkins等,如果来了新员工就要在每个系统里添加一个账户,离职了要把所有账户注销,这个工作量太大。所以考虑引入一个通用的单点登录系统,现在比较流行的应该就是CAS了,但是CAS Server版本是java写的,而我们公司是PHP技术栈,如果想要调整或者新增一些CAS的功能就不太好办,所以就花了一点时间根据CAS协议实现了一个PHP版本。

  • Leo Chen
    Leo Chen
PHP

PHP的错误和异常处理总结

PHP的错误和异常处理总结 PHP内置了一批与错误和异常处理相关的函数,本文会对其中部分函数进行详细说明。 set_error_handler 和 restore_error_handler set_error_handler可以设定当程序出现错误时,将对应的错误交给用户自定义的逻辑来处理。 但是并不是所有的错误都可以被set_error_handler所指定的处理逻辑捕获,例如:E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING,简单来说就是原本就会导致程序终止的错误都无法被捕获。 set_error_handler只能设置一个错误处理逻辑,多次调用set_error_handler只有最后一次的那生效: set_error_handler(

  • Leo Chen
    Leo Chen
mock

Centos下使用mock构建rpm

在centos下执行rpmbuild -ba package.spec的时候,通常会要求系统也安装对应的依赖包,这样就会导致系统装上许多没用的软件包,占用空间。于是搜索了一下,发现可以通过mock命令来解决这个问题。 首先是安装mock,由于mock是在epel仓库里的,所以还需要先装epel仓库 yum -y install epel-release yum -y install mock 通常情况下使用rpmbuild会新开一个用户,比如builder,这样就不会污染系统环境。我们需要把builder用户加入mock用户组 usermod -a -G mock builder mock下使用rebuild需要src.rpm文件,所以如果只有.spec文件的话,需要先生成src.rpm文件 rpmbuild -bs package.spec 这样在SRPM目录下就会生成一个src.rpm文件了,然后就可以通过mock命令来rebuild rpm文件 首先需要初始化mock环境,

  • Leo Chen
    Leo Chen
symfony

Symfony支持多个站点(应用)

对于一个网页系统来说,通常会需要面向用户的站点和面向管理员的站点,有的甚至还需要面向App的api站点。这些站点拥有不同的域名,但却共享核心业务逻辑。 Symfony的标准发行版只支持一个站点,虽然可以通过路由系统中的Host配置,根据不同的域名使用不同的路由规则,这样也可以实现类似多站点的功能,但缺点也非常明显: 如果希望某个Service在不同的站点有不同的表现,就没办法实现(DI不能直接注入Request)。 静态文件没办法很好拆分开来 每个页面请求都需要加载所有站点的配置(bundle、路由规则、Service等等),影响性能 不同的站点的异常处理逻辑不同(例如对于NotFoundHttpException,在Api站点可能需要输出一个json串而在网页端需要输出一个404页面) 经过搜索,发现也有人有相同的困惑,也给出了一个初步的解决方案。但是还是有一些细节方面的问题,比如标准发行版自带的Composer post-install-cmd/post-update-cmd(清文件缓存、生成bootstrap.cache.php、发布静态文件到web根目录等)不能正常使用。那篇文章只是通过软链解决了bootstrap.cache.php的问题,但并没有提到清文件缓存等。 这个问题只能自己写代码来解决了,新建一个composer项目,依赖于sensio/distribution-bundle,新建一个ScriptHandler类,

  • Leo Chen
    Leo Chen
symfony

vagrant环境中symfony程序速度慢解决方案

最近在写一个symfony程序,最开始是直接在Mac下通过console server:run命令启动一个简单的web服务器来访问,但是Mac本身自带的php没有memcached扩展,所以就把这个程序放到vagrant中,然而发现访问的速度非常慢,一个极其简单的页面也需要消耗10秒左右,于是搜索了一下,发现之前有人遇到相同的问题。 该文章中列出的几个方案: 使用vagrant1.2版本(目测不合适,现在都已经1.7+了) 使用NFS方式挂载目录 vagrant虚拟机中的Vbox Guest Additions版本与virtual box版本一致(我当前的环境就是一致的) 使用opcache扩展(我当前的环境已经安装了apc) 关闭xdebug和xphrof扩展(我当前环境已经关闭) 看这情况,只能尝试一下NFS方式了,根据vagrant的文档配置,还好OS X自带了nfsd,省去了安装的麻烦,只需要修改vagrantfile即可。 在vagrantfile中增加两行: config.vm.network :private_network, type: :dhcp config.vm.

  • Leo Chen
    Leo Chen
PHP

PHP程序配置文件(最佳?)实践

最原始的方式 写php程序时会直接把数据库、缓存的连接信息放在config.php文件里。这样做有两个弊端,1.在开发调试时必须先把连接信息改成本地的,要提交代码时再改回远程的,麻烦,也容易遗漏。2.开发人员可以直接看到线上数据库的连接地址、账号、密码,不安全。 怎么办? 线上运行PHP程序时通常需要一个Http服务器,例如apache、nginx。以nginx为例,在配置文件中可以通过fastcgi_param指令来给PHP传递变量 fastcgi_param DB_HOST "192.168.1.1"; 这样就可以在PHP代码里通过$_SERVER[‘DB_HOST’]来获取到对应的值。 所以开发环境和线上环境只要配好nginx的配置,就可以实现不改代码执行程序了。 更进一步,可以传递一个标示当前环境的变量,例如 fastcgi_param

  • Leo Chen
    Leo Chen
centos

vagrant centos升级内核版本、升级VBoxGuestAdditions版本

在vagrant的centos中,如果直接执行yum install kernel-devel,会提示”No matches found for: kernel-devel”,仔细观察了一下yum的输出,发现加载了一个versionlock的插件,于是猜测与这个插件有关,禁用了内核版本的更新,所以把这个插件禁用掉即可。 编辑/etc/yum/pluginconf.d/versionlock.conf文件,将enable的值改成0。然后再执行yum update kernel就可以将内核更新到最新版本。 但是这个时候如果重启了vagrant虚拟机,会发现vagrant报错 Failed to mount folders in Linux guest. This is usually because the “vboxsf” file system is

  • Leo Chen
    Leo Chen
centos

将git源码打包成rpm安装包(centos)

centos6系统里通过yum安装的git版本只有1.7.1,好多新特性都没有。而使用编译安装的话,每台服务器都要编译一遍太麻烦,以后更新起来也麻烦,所以决定自己打一个rpm包。 首先需要安装rpm-build yum install -y rpm-build 然后创建一个rpmbuild目录: cd ~ && rpmdev-setuptree 到https://github.com/git/git/releases下载git源码包,一定要下载.tar.gz版本的,rpmbuild需要这种格式。 先下载到home目录下 wget https://github.com/git/git/archive/v2.6.4.tar.gz -O ~/git-2.6.

  • Leo Chen
    Leo Chen
QrCode

如何在linux的终端输出二维码

脑子突然间蹦出来的想法,就迫不及待试试看能否实现。 需求很简单,就是在linux的终端中输入一个字符串(可以是以命令行参数形式,也可以是通过交互式输入),然后就会输出对应的二维码。 首先PHP已经有现成的QrCode类库phpqrcode,可以将一个字符串转成PNG格式的图片,但是PNG图片是没法在终端里展示的,于是仔细翻看文档和demo,发现该类库也可以输出0和1组成的矩阵(实际上该方法返回的是一个PHP的二维数组)。 已经有了0和1的矩阵,接下来要做的就是输出黑白色块,为了操作方便,我引入了symfony项目中的console组件。通过console组件可以非常方便的创建一个Cli命令,而且内置了大量输入和输出方法。 根据console的文档,我们可以新建两个OutputFormatStyle: $black = new OutputFormatterStyle('black', 'black'); $output->getFormatter()->setStyle('blackc', $black); $white = new OutputFormatterStyle('white', 'white'); $output->getFormatter()->

  • Leo Chen
    Leo Chen
闽ICP备19022043号-1