Failed to start php-fpm.service: Unit php-fpm.service is masked.

升级服务器到 Ubuntu 18.04 后,php 的源也升级到 7.2,此时 php7.1 的一些模块已经没了,所以要换用 7.2 的功能。

周末手一抖把服务器从 17.10 升级到了 18.04,然后博客就挂掉了。

根据提示信息,nginx 应该正常工作,问题多半出在 PHP 上。service --status-all 之后,果然 php7.1-fpm 没有启动。然后照常 service php7.1-fpm start,咦,奇怪,报错了:

Failed to start php7.1-fpm.service: Unit php7.1-fpm.service is masked.

换用 service php-fpm start 也一样,区别就是 service 名字不太一样。然后 Google 之,没找到很靠谱的说法,但是找到一个类似的情况,发生于使用 do-release-upgrade 升级到 16.04 时,php5-fpm 启动不了,报类似的错误,解决方案是升级到 php7。

如此一来我也试试好了,因为直接 apt install php-fpm 会解析出来 php7.2,所以我尝试 service php7.2-fpm start,果然可以。既然如此,干脆升级到 7.2 好了,反正我也没啥特殊要求。

于是修改站点配置文件,把 php 接口指向 7.2 的 socket,然后安装几个欠缺的模块,终于又把博客跑起来了。

MySQL 的编码问题

解决字符集为 utf8_general_ci 的表里无法存储表情符号的问题。

前几天朋友的小程序遇到点问题,同样的代码,有些人就是注册不上,有些人就没问题。让我帮忙看。

看代码应该没问题,我自己试也没问题,很诡异。后来我发现,说注册不上的一个截图里,昵称里有一个雨滴的符号。我们知道,传统的编码是没有表情符号的,表情符号是 Unicode 后期才加进去的,那么会不会是数据库字段的问题?看了一眼,Laravel 默认创建的数据库,字符类型是 utf8mb4_unicode_ci,而他们数据库是 utf8_general_ci,Google 一下,找到下面这篇文章:

為什麼MYSQL要設定用UTF8MB4編碼 UTF8MB4_UNICODE_CI

里面提到:

當資料庫需要儲存或處理以下資料:emoji (手機端常用的表情字符)

应该使用 utf8mb4_unicode_ci,因为它会用更多的空间存储字符。基本锁定是字符集的问题。然后看到梦康大的一篇博文:直接使用 mysql utf8 存储 超过三个字节的 emoji 表情 ( 不使用 utf8mb4 ),决定参考他的方案,毕竟改库改表不是小事情。

不过时过境迁,梦康文中的 func_overload 已经可以用 mb_strlen($str, '8bit') 来替代,所以最后的代码大约是这样的:

// 替换
protected function encodeEmoji($input) {
  $length = mb_strlen($input, 'utf-8');
  $result = '';

  for ($i = 0; $i < $length; $i++) {
    $tmp = mb_substr($input, $i, 1, 'utf-8');
    if (mb_strlen($tmp, '8bit') >= 4) {
      $result .= '[[Emoji:' . rawurlencode($tmp) . ']]';
    } else {
      $result .= $tmp;
    }
  }
  return $result;
}

// 替换回
protected function decodeEmoji($nickname) {
  return preg_replace_callback('~\[\[Emoji:(.*?)\]\]~', function ($matches) {
    return rawurldecode($matches[1]);
  }, $nickname);
}

所以说,程序员心态要保持年轻,步调要跟年轻人保持一致,这样才更容易发现新问题,所以我的昵称已经改成“肉山🎩”了。

Laravel 开发笔记:环境搭建

记录 Laravel 的一些笔记,随时更新。

文档

英文官网:https://laravel.com/
英文文档:https://laravel.com/docs/5.5
中文文档:https://d.laravel-china.org/docs/5.5/packages

建议两边都打开,对比阅读。

配合 PHPStorm 使用

Laravel 里面定义了很多别名,直接放在 PHPStorm 里会有很多黄色曲线,看起来非常不爽。关键是影响代码补全,所以最好用一个库来搞一下。

安装 barryvdh/laravel-ide-helper:

composer require --dev barryvdh/laravel-ide-helper

注册服务。编辑 app/Providers/AppServiceProvider.php:

public function register() {
  if ($this->app->environment() !== 'production') {
    $this->app->register(IdeHelperServiceProvider::class);
  }
}

生成 meta 文件:

php artisan ide-helper:meta

诡异的 Mac + PHP + Nginx 问题

前两天又遇到一个诡异的问题,以上是解决手段。

问题描述

这次是微信公众号开发,本身就有很多问题,也严重加剧了我调试的难度。正在做一个 WordPress 插件,功能是将公众号文章和 WordPress 互相同步。之前一切正常,突然有用户反馈抓不到公众号上的文章了。

顺便说下,插件地址:https://github.com/meathill/wp-plugin-weixin 目前支支持手动部署,完成二维码之后开始做发布版。

继续阅读“诡异的 Mac + PHP + Nginx 问题”

Ubuntu 16.04 搭建 LNMP 开发环境

根据软件版本变化不断更新,如何在 Ubuntu 上搭建 LNMP 环境,以及可能遇到的坑,以及如何进行安全性设置。作为本博客第一篇收费文章,看能不能收到钱。

前天帮人配了台机器,未来可能还要帮人配。在学会用 Docker 之前,先写一篇记录下怎么搭建环境吧。

这篇收费!¥4.99,请阅后自觉打钱。

继续阅读“Ubuntu 16.04 搭建 LNMP 开发环境”

使用 Satis 搭建私有仓库

这篇文章介绍如何使用 Satis 搭建私有源。Composer 是现在最常用的 PHP 依赖管理工具,然而 PHP 的特点决定了它对安全性的要求很高,把依赖放在开放仓库里可能会带来各种危险,所以建立私有源就显得非常必要。Satis 是官方提供的工具,简单好用。本文除了介绍文档中的搭建流程,还会分享我踩过的坑。

题外话

开始正题之前聊一些题外话。PHP 是个很好的语言,简洁高效,易学强大。但是出身不好,工程学上的高起点反而成为大家轻视它的原因。很多开发者也的确对自己要求不高,只写业务逻辑不考虑语言特性,使得代码难看难改难维护。所以我想多说两句回顾一下 PHP 本身的发展史。(以下以我个人经历为主)

上古时期

我们一个页面写一段 PHP,或者一个动作写一个 PHP,收集请求,做出处理,给出回应,完成。

好处:

  1. 简单,好上手
  2. 逻辑关系清晰,从前端可以直接找到目标程序
  3. 一个地方出错,多半只挂一个功能

坏处:

  1. 代码复用率低,不好维护
  2. 难以批量修改

古典社会

随着工程变大,需要大量的 PHP,分散碎片化的代码实在难以管理和维护。于是我们开始把一些共用代码抽出来,做成一个函数,叫 functions.php,其他所有页面都 include 它,这样公用的代码就不会这里一份那里一份了。

好处:

  1. 提高了代码复用性,减少开发量,提升效率,降低维护难度

坏处:

  1. 工程大的话,一个 functions.php 好几千行,可读性也没好到哪儿去
  2. 有时候我们需要对一个函数进行一些小修改,于是不仅函数库会膨胀,函数本身也在膨胀

中世纪

PHP 引入类的概念,并且提供了“魔术方法”来实现一些功能。有些程序员也意识到不能所有代码都自己手写,该引用的还得引用,于是从一些开源的库拷来代码开始用。这个时候连 Google Code 都不存在,下载代码多半在网上搜索 + Ctrl C/V,所以代码中各种混用。经常出现,我 include 一个文件,然后就挂了,原来是类被重复定义,或者全局环境下同名函数互相覆盖。开发乱象不断,形如黑暗的中世纪。

好处:

  1. 不考虑维护的话,开发速度还是可以的……

坏处:

  1. 越到后来坑越多,项目一大积重难返
  2. 内部执行环境不统一,a.php b.php 的内部环境都不一致,共享代码反而更加困难

文艺复兴

PHP 对类的支持已经十分完善了,大家也开始习惯用命名空间划分领域。通过使用设计模式、继承、接口,复写功能和代码管理的情况大大改善。同时,伴随 Google Code 和 GitHub 的出现和发展,大家有了一个托管代码和寻找代码的好地方。我们也开始用 SVN 管理代码,不会再搞出 action.php action.php.bak action_new.php action_new.20160102.php 这样的幺蛾子。开始学习开发规范,开始更多的的用类管理代码。

好处:

  1. 代码规范
  2. 版本管理后,更好追溯代码的变更记录
  3. 可以下载到新版本的代码

坏处:

  1. SVN 不方便进行多仓库的管理
  2. 测试还靠人工发掘问题

近代社会

Git 开始普及,我们可以更方便的管理代码了。GitHub 发展速度很快,从上面找好代码也很容易,凭借 Git 子仓库的概念,维护依赖也容易很多。MVC 框架开始普及,单入口开始流行,内部执行环境得到统一。开发者意识到测试的重要性,开始使用测试工具进行测试开发,代码的稳定性进一步提升。

好处:

  1. 内部执行环境统一,全局修改变得容易
  2. 开始写测试了

坏处:

  1. 学习成本开始增加,新入行的人开始搞不懂,为啥写一个脚本就能干的事,你们要搞这么复杂一套架构出来

现代社会

包管理工具成为标配。项目依赖不再通过复制代码或者子仓库来管理,而是直接使用包管理工具 Compposer。并且整合测试、部署脚本,方便我们更容易地完成整套开发流程。另一方面,前端之前已经崛起,PHP 可以更多的考虑后端业务逻辑,输出纯粹的数据接口。

好处:

  1. 大型项目稳定性可用性大大增加
  2. 专业分工加强,PHP 程序员可以更多考虑后端逻辑

坏处:

  1. 学习曲线更加陡峭,新人入行更难,甚至连有经验的老人都未必能适应新形态的开发。

然则历史的车轮不可阻挡,我们势必会走向学习成本更高、学习曲线更陡,但业务量更大、更稳定的未来。


正文开始

正如前面所说,现在我们更多使用 Composer 进行依赖管理。和其它语言的包管理工具一样,Composer 使用 GitHub 托管代码,可以根据配置文件管理依赖,也可以建立各种脚本,执行特定任务。总之好处很多。

实际工作中,我们可以把多个项目公用的逻辑抽出来,作为一个依赖,然后提交到 Packagist,就可以在其它项目中引用它了。但是,与 NPM 这种工具不同的是,PHP 程序多半会部署在服务器上,通过接口接受外部访问,对安全性的要求高很多。前端可以放开给大家随便观摩,后端最好还是放在别人轻易看不到的地方,万一哪个同事把密码、salt 写到代码里提交,被搜出来,结果可能就很危险。

此时我们就需要一个工具,能够搭建私有源,里面都是私有仓库,对内不对外。

Satis 就是 Composer 官方提供的建立私有源的工具。它的文档在这里 以及 这里

整体流程并不复杂,文档里都有,我简单复述一下,只包含我用过的部分,重点穿插我的经验。我假定读者已经了解 Composer 的基础使用,如有问题,请自行翻阅文档。

1. 建立项目

使用 Composer 自带的建项目功能,这个相当于 git clone + composer install + 运行 post-install 脚本。

composer create-project composer/satis my-satis --stability=dev --keep-vcs

2. 建立配置文件

/path/to/my-satis 目录下建立 satis.json 文件

{
  "name": "仓库名称",
  "homepage": "http://satis仓库地址",
  "repositories": [
    { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
    { "type": "vcs", "url": "http://svn.example.org/private/repo" },
    { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
  ],
  "require-all": true
}

注意:仓库名称需要和仓库里 composer.jsonname 定义一致,和路径没什么关系,不然就会找不到。我当时被这个卡了好久……

因为加入私有源的仓库本身可能也有依赖,require-all 会把这些依赖的信息也抓进来。如果不需要的话,可以指定某个仓库,甚至某个版本:

{
  "name": "仓库名称",
  "homepage": "http://satis仓库地址/",
  "repositories": [
    { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
    { "type": "vcs", "url": "http://svn.example.org/private/repo" },
    { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
  ],
  "require": {
    "company/package": "*",
    "company/package2": "*",
    "company/package3": "2.0.0"
  }
}

3. 生成仓库列表

执行

php bin/satis build satis.json ./web

就可以在 path/to/my-satis/web/ 里生成仓库列表了。

4. 在其它项目中使用私有源

只需要在项目的 composer.json 文件的根上添加

{
  "repositories": [
    {
      "type": "composer",
      "url": "http://satis仓库地址/"
    }
  ],
  "require": {
    "company/package": "1.2.0",
    "company/package2": "1.5.2",
    "company/package3": "dev-master"
  }
}

之后再通过 composer require 或者 composer install 想要的仓库就可以了。

注意:源里面只有“仓库列表”,并没有真的同步代码仓库过来,所以下载还要走托管代码的机器,比如 GitHub,内部 GitLab 等。所以需要确保相关的 ssh-key 已经添加,或者在配置文件中写上登录信息(不建议这么做)。

Tips: secure-http

satis 默认要求使用 https,不过 https 需要证书,不太好搞,比如前司运维就不愿意弄(当然,他们工作很忙,我十分理解)。此时我们可以设置 secure-httpfalse 强制 Composer 接受 http 的源。需要注意,secure-httpconfig 的属性之一,写在根上是没用的。

{
  "config": {
    "secure-http": false
  }
}

总结,Satis 私有源的搭建,对于使用 PHP 的开发团队来说是非常必要的。用 Composer 管理依赖效果也非常好,希望所有 PHP 开发者都好好学一学。我现在用的也比较浅,将来有心得继续补充。

使用 Windows Linux Subsystem 配置开发环境

最新版的 Windows 10 提供了”Windows Subsystem Linux“功能,可以在 Windows 下跑 Linux,大大提升开发效率。本文将介绍如何在 WSL 下搭建 PHP 开发环境。

微软前些日子公布了 Windows Subsystem Linux(下简称WSL),当时还只有 Insider Preview 支持安装,作为普通用户的我就没有尝试。这两天不知道伴随哪次更新,标准的专业版也可以安装了,就试着配了一下,发现效果还比较理想,基本可以替代之前的 IIS 或者 VirtualBox 方案。这里简单记录一下。

(2016-12-02 更新)随着最近一次 1607 更新,家庭版也可以安装了,效果基本一样。
(2018-01-21 更新)已知 Yarn 在非 ~/ 开头的目录里 install 会出问题

安装 WSL

开始 > 设置 > 系统 > 应用和功能 > 拖到最下面“程序和功能”。然后点击“启用或关闭 Windows 功能”,找到“适用于 Linux 的 Windows 子系统(beta)”,启动之。

启用 Windows Subsystem Linux

之后系统会要求重启,听它的。

然后打开命令行(或者 PowerShell,下同),输入 bash,提示需要切换到开发者模式。按照提示切过去,然后再次运行 bash,系统会安装需要的组件。这个过程会比较久,大概源都在国外吧。安装完成后又要重启,之后 WSL 就算安装成功。

再次打开命令行,输入 bash,就可以进入 Linux 子系统了。

WSL 其实就是 Ubuntu 14.04.5,所以安装包直接用 apt-get 就可以。嫌慢的话可以换上国内的源,不赶时间就让它慢慢更新。

sudo apt-get update
sudo apt-get upgrade

配置Apache

这次我本来打算用 Nginx,装上之后启动不了,Google 之,似乎是 WSL 对 IP v6 的支持有问题,遂卸掉继续用 Apache。反正本地开发嘛,性能并发不是什么问题。

Ubuntu 自带 Apache 2.4.7,直接 sudo service apache2 start 启动就好。不过这里遭遇到 vhost 的问题,我配置的虚机怎么都不生效,最后在 stack overflow 上找到解决方案,说是 000-default 和自定义的主机冲突,禁用掉默认配置就好了。

sudo a2enmod vhost_alias
sudo a2dissite 000-default
sudo a2ensite 001-mysite

然后还需要调整一下模块,比如要进行 WordPress 开发,就要保证重定向生效。

sudo a2enmod php7.1
sudo a2enmod rewrite

另外,Apache 2.4 要求配置目录的“访问条件”,不然不能访问(会返回 “403 Forbidden”,改目录权限也没有用),所以要增加一行:

<Directory "/path/to/my/site">
  ...
  Require all granted # 允许所有来源
  AllowOverride All # 允许重定向,WordPress 必备
  ...
</Directory>

全部配置好之后,重启 apache,访问配置的域名,就可以了。

安装 Nginx

不知道什么时候 Nginx 可以用了,反正我某天试着配了一下,突然就可以用了。

sudo apt install nginx

安装完配置一下即可,不过需要注意,必须关掉 Apache,避免出现端口冲突。

安装 PHP

添加 PHP 源。

sudo add-apt-repository ppa:ondrej/php

需要注意,以前那个 ppa:ondrej/php5-5.6 的源已经被移除了,5.5 到 7.1 都用 ondrej/php 这个新源。如果非要用 5.4 的话,还有个 php5-oldstable 的源。

这里顺便把几个可能用到的组件也装上。

sudo apt-get install php
sudo apt-get install php-mysql
sudo apt-get install php-xdebug
sudo apt-get install php-apcu
sudo apt-get install php-xml
sudo apt-get install php-mcrypt

配置 Xdebug

配置好 PHP 之后,开启 Xdebug 并不复杂。以我安装的 PHP7.1为例,安装之后,配置文件都在 /etc/php/7.1/apache2/conf.d/ 这个文件夹里,找到 20-xdebug.ini 并添加以下几行,然后重启 apache,即可。

xdebug.remote_enable=on
xdebug.remote_autostart=on
xdebug.idekey=PHPSTORM

使用 cmder 替换 PowerShell

PowerShell 也好,自带的命令行也好,都不好用。要么字体难看得要死,要么中文输入有问题,而且不支持多窗口。这里推荐 cmder,很好用,体验提升明显。

cmder 支持打开窗口并执行启动脚本,可以一键进入 Linux 控制台,只要按下图进行配置即可。

配置 cmder 支持 WSL

然后再修改启动时默认的 tab。

修改 cmder 启动时默认打开的 tab 类型

启用 Ruby

这个版本的 Ubuntu 集成了 rvm,通过 rvm list 可以看到已经安装了 Ruby 2.3.0。但是直接运行 ruby 却提示没有安装,运行 rvm use 2.3.0 提示

RVM is not a function, selecting rubies with ‘rvm use …’ will not work.
You need to change your terminal settings to allow shell login.
Please visit https://rvm.io/workflow/screen/ for example.

这个很奇怪,以前没有遇见过。通过 Google 得知,这是因为默认的登录方式没有读取 ~/.bash_login 里的配置信息,没有加载到 rvm 需要的方法,所以需要在启动 WSL 的时候增加参数 --login,所以回到上一步,修改启动脚本,问题解决。

%windir%\system32\bash.exe ~ -cur_console:p --login

(这里我还去掉了zsh,因为这台电脑似乎没有安装这个包,我不知道是不是默认不安装的问题。)

安装 MySQL

[2017-03-25 更新] 下面提到的坑跟我用“旧”配置——0.6.0 有关。这个配置参考自 digital ocean 的文章,实际上 Oracle 已经推出了新版本的配置文件,使用新版配置文件安装 MySQL 就不会遇到这个问题。新版配置文件的链接在此。其它步骤相同。

原以为安装 MySQL 最简单,没想到也有坑……主要是 5.7 的坑。

我比较喜欢新版本,所以自然就准备上 5.7。Ubuntu 自带的源是 5.5 或者 5.6,所以要先更新一下:

wget http://dev.mysql.com/get/mysql-apt-config_0.6.0-1_all.deb
sudo dpkg -i mysql-apt-config_0.6.0-1_all.deb
sudo apt-get update

好了,接下来直接安装就好:

sudo apt-get install mysql-server

根据提示信息安装 MySQL Server 5.7,设置 root 密码。这个阶段多半不会出问题。安装完成之后,运行 mysql -uroot -p,输入刚才设置的密码,不出意外的话就可以进入 MySQL 控制台了。

进入 MySQL 控制台

接下来我们通常要设置开发用户,此时问题也就来了。(这个问题在我的几台机器上,包括 VPS 表现不一,时有时无,所以我不太确定到底什么情况下会发生。)

CREATE USER 'someone'@'localhost' IDENTIFIED BY 'password';

执行这句 SQL 将创建一个用户,看起来完全不会有问题,结果却报错了:ERROR 1054 (42S22): Unknown column ‘plugin’ in ‘mysql.user’(报错的列可能不同,但原因是一样的)。Google 之,原来随着 MySQL 版本升级,系统内建表的结构也发生着变化,5.7 需要的字段比起 5.5 甚至 5.4 多了不少。虽然我们直接安装的 5.7,也不耽误它按照老的结构创建表……

这个时候有两个选择,一是手工把差的列加上,另一个则是运行官方提供的升级脚本 mysql_upgrade。为表达对官方的信任,我选择了后者。退出 MySQL,回到 SSH,执行脚本:

mysql_upgrade --upgrade-system-tables -u root -p

升级完成之后,再 mysql -uroot -p,输入密码,登录不进去了!WTF!怎么回事?!

又是一通 Google,原来 5.7 之后,开发团队移除了 mysql.user 表里的 password 字段,用 authentication_string 取而代之,然而这个字段并没有复制之前 password 的值,所以原来的密码就失效了!这是什么鬼……

这里我们又有两个选择,一是想办法修改 root 密码,二是卸了重装……因为我当时没找到真正的原因(就是上面这个),所以选择了后者。现在我推荐一个解决方案:

  1. 安装后,进入 MySQL 控制台,输入 DESC mysql.user;
  2. 如果返回 45 rows(行),则不需要升级系统内建表,改干嘛干嘛;如果少于这个数,就要执行 mysql_upgrade
  3. 刚才的窗口不要动,新开一个窗口,执行 mysql_upgrade --upgrade-system-tables -u root -p
  4. 执行完毕回到刚才的窗口,在 MySQL 环境下执行
UPDATE `mysql`.`user`
SET `authentication_string`=PASSWORD('password')
WHERE `user`='root';

文件互访

在 WSL 里访问系统中的文件比较容易,使用 /mnt/盘符/路径 即可。

如果要在 Windows 里访问 WSL 里的文件,它们位于 C:\Users\你的用户名\AppData\Local\lxss\

有待解决的问题

有了 WSL,各种依赖 Linux 环境的东西都可以随便跑了,利用 apt-get 安装更新也都很方便。侦听端口要完全没有问题。但其实两个环境是隔绝的,Windows 里不能执行 Ubuntu 的东西,反之亦然。这样一来,诸如 Phpstorm 的 File Watcher 包括测试都无法正常发挥作用。解决方案有待进一步发掘。

临时解决方案就是通过命令行启动各种 watch,比如

sass watch

Yarn

在非 ~/ 开始的目录里执行 yarn install 有一定机率会产生错误,具体原因不明,暂时无法稳定重现,反复重试之后倒是也能装成功。这个是 WSL 的锅,只能等待 Windows 更新。

总结

时代在发展,科技在进步。按照 GitHub 刚刚发布的报告,微软已经是开源贡献第一大公司,Windows 拥抱 Linux 之后我们也可以在 Windows 下进行PHP开发了。世界真美好啊!

顺便Ubuntu新版本也比老版本容易配置了,当然,也可能是我之前不会。


参考:

The main PPA for PHP (5.5, 5.6, 7.0) with many PECL
Support for the new Windows 10 bash shell
手把手安装 RVM 以及为什么 RVM is not a function
How To Install MySQL on Ubuntu 14.04

使用 Phantomjs 导出 PDF

使用 Phantomjs 可以很方便的导出 PDF,并且几乎可以在导出前进行各种编辑调整,使其满足要求。

接到一个新需求,给用户导出电子协议,也就是 PDF 文档。因为我们后台使用 PHP,所以自然就去寻找 PHP 的解决方案。看了几个库,包括 Packagist 几万十几万下载的库,唉,不得不说,虽然 PHP 是世界上最好的语言,但是 PHP 开发者,审美水平真的,说好听点就是,没法看……

第二个问题是,PHP 很多都要“拼” PDF,一行一行,一个元素一个元素,组装起一个完整的文档。相当费力,而且样式不好控制。从 HTML 转换也有类似的问题。研究了一会儿觉得蛋越来越疼,唉,算了,还是换 Phantomjs 吧。

Phantomjs 是一个命令行 Webkit 工具,我们可以把它理解成不输出页面的浏览器,但它支持浏览器的各种功能,因为有 Webkit 嘛。所以渲染网页然后抓图就是小菜一碟了。

用 Phantomjs 输出 PDF 非常简单:

  • 首先,约定好宽高(为方便打印,我们的电子协议要分页,而且有页头页脚),完成页面模板。
  • 完成 Phantomjs 脚本。因为只用它生成文档,所以不需要 Web 服务。
  • 用 PHP 调用脚本,生成 PDF 文档,然后 readfile 给用户下载。

这样做的好处是我们随时可以预览效果,HTML 好读好改,PHP 替换其中的内容也很方便。而且代码非常简单,结合官方示例,很快就写出来了:

'use strict';

var page = require('webpage').create()
  , system = require('system')
  , args = system.args
  , url = args.length > 1 ? args[1] : 'http://www.dianjoy.com/'
  , filename = args.length > 2 ? args[2] : 'tmp';

page.viewportSize = {
  width: 800,
  height: 1100
};
url = decodeURIComponent(url);
page.open(url, function (status) {
  console.log(status);
  if (status === 'success') {
    page.render('/tmp/pdf/' + filename + '.pdf');
  }
  phantom.exit();
});

部署这段代码最大的问题反而是 GFW 导致 npm install phantomjs -g 失败,直接下载 zip 也不行(因为放在 Amazon S3 上)。于是继续给病魔加油,早日弄死方校长,及其它筑墙士。

第二个问题则是 PHP 执行脚本老不成功。只看文档很简单:

exec('/usr/local/phantomjs/bin/phantomjs pdf.js http://meathill.com/ meathill');

但实际上既没有生成文档,也没有任何返回,调试半天,我突然想起来前阵子用 Apktool 解析安装包的时候也遭遇过类似的问题,于是在末尾加上 2>&1 问题竟然就解决了。

Google 之,也不是很懂。回头再说吧。


其它参考:

shell_exec


图文无关。其实是一位大学友人今日喜得贵子,放张她的奇怪照片祝贺她。

解决 PHP 导出 CSV 的乱码问题

PHP 输出 CSV 文件时,如果是 UTF-8,需要在前面加上开始标记才能让 Windows + Office 正常识别。

项目当中遭遇一个奇怪的问题:

导出 CSV,文本编码使用 UTF-8,使用 Mac + Numbers,Windows + WPS 打开都正常,使用 Windows + Office 就乱码(Mac + Office 没有测试)。用记事本打开另存为,编码的确是 UTF-8。

后来发现,用 EditPlus++ 打开,然后另存为 “UTF-8 BOM”,就可以正常打开了。看来应该是这个 BOM 的问题。

于是乎参考 StackOverflow 这个答案,给输出的开头加上 "\xEF\xBB\xBF",果然解决了问题。

PHP匿名函数使用父作用域的变量

PHP的匿名函数可以使用 use ($var) 来使用父作用域的变量。

(自古图文不相关)

同事问为什么要用 array_maparray_filter之类的函数,用 foreach 不就好了?

答:这样写出来的代码语义更清晰,阅读更容易。

那么如何使用其它变量呢?global 么?

答:global 肯定不合适,不过怎么写我也不知道。待我查查。

在PHP中,不能像JS那样直接使用闭包里的其它变量,必须通过声明继承的语法,写出来是这样的:

    <?php

    $arr = [1, 2, 3];
    $split = 2; // 分界
    $arr = array_map(function ($value) use ($split) {
      return $value < $split ? 0 : $value;
    }, $arr);
    var_dump($arr);

    // 输出
    // 0, 2, 3

重点是那个 use ($split)


参考: