Inspirer

PHP Cli 程序开发

PHP 在大多数人心目中主要是做 web 开发的,但实际上从 PHP 4.2.0 起就开始支持 Server Application Programming Interface 即服务端应用编程接口,也称其为 Command Line Interface,也就是我们常说的 CLI。这使得 PHP 开发外壳应用(Shell)变为可能。其实这个功能自 PHP 5 起变得越来越易用,这对于 PHP 应用于更多情景变得可能,比如多进程爬虫、服务端守护进程、自动化部署工具等等,我们如今使用的 composer 就是典型的 PHP CLI 程序。

本文主要内容就是围绕 PHP CLI (下文简称 CLI) 进行讲述,并分享一个十分强大且易用的 symfony 组件 —— console (命令行)组件。

先谈谈 CLI 程序

很多初学者总是认为 PHP 就是做 web 开发的,也只能做 web 开发,也正是这样普遍的认识导致了 phper 素质的普遍低下以至于被无限黑。我们需要知道,CLI 的出现意味着 phper 可以用无比简单的 PHP 做更多的事情,虽然很多情况下 PHP 效率不高(我没说 PHP 7 哦),但不意味着命令行程序变得鸡肋,事实上,在学习 PHP 时,CLI 是最应该被推崇的方式,主要原因就是其不需要过多的配置环境,甚至根本不需要。

很多人在初学 PHP 花费了大量时间去熟悉环境配置,或者最终妥协选择一键安装包(我很不喜欢这个玩意儿),导致学习的乐趣大大降低,搞了半天也没什么成就感。其次就是每一次代码修改都要浏览器反馈,而浏览器显示出来的东西在没有样式表(css)和合理的 html 结构下很难看不说,并不能即时反馈出输出的过程。

相反,命令行程序更易于学习,只需要编写好代码,保存成文件,在 shell 下输入 php <php 文件名> 即可立即看到结果。看到这里肯定有人跃跃欲试,但是为了后文便于展开,还是要说一下环境的配置。

环境配置

之前说的环境配置可以没有,不代表你不需要做任何事,至少你得先下载 php 对吧?

Windows 平台简直不能再简单,直接到 http://windows.php.net/download 页面下载所需要的版本即可,然后解压至任意目录,将该目录添加至环境变量,然后再命令行窗口下输入 php -v 是否正确显示 PHP 版本信息,如果正确显示则环境配置完毕。

Linux 系统下可通过 apt-get、yum 等工具安装,安装好后将 php 的 bin 目录加入环境变量即可(有的会自动加入)。也可以通过编译安装的方式安装,具体编译安装方法可参考该博客的文章:https://www.insp.top/compile-and-install-apache24-and-php56-on-centos 进行。安装完毕后将 php 目录中的 bin 目录加入环境变量。在 shell 下输入 php -v 检查无问题即可。

第一个 CLI 程序

相当简单,创建并编辑文件 cli.php

<?php
echo "hello, world";

然后再命令行输入 php cli.php,运行结果估计都知道了。这里大家已经明白了怎么开发并使用命令行程序,其实和大家做 web 开发方式完全一样,只是执行并展示结果的过程不一样。而且,CLI 程序和 web 程序并不会冲突,无论是环境配置还是其他,方式完全一样,只是 web 开发还需要单独配置 web 环境的东西如 nginx、apache、iis 之类的家伙。

CLI 与 web 程序

大家估计到这里已经明白了如何去写 CLI 程序,但是还是有一些人不明白这东西的原理。

实际上,无论是什么系统平台,PHP 的程序都是通过 PHP 解析器执行,解析器在 Windows 平台下是 PHP 目录中的 php.exe 文件,在 Linux 平台下是 php 安装目录的 bin 目录下的 php 文件,这也是我们为什么将其所在的目录纳入环境变量下,这样就可以全局执行。

严格意义上讲,所有 PHP 程序都要经过这个解析器,无论是 CLI 还是 web 程序。只是对于我们常见的 web 程序而言,调用解析器的工作交给了 web 服务端程序(apache、iis、nginx 等),所以本质上 CLI 和这些 web 程序没太大区别,要真的说区别也仅仅在于 web 程序会获得来自 web 服务器程序提供的一些相关变量如 $_SERVER 等。

2015.9.14 补充:为了便于一些人注意到重点,我在此单独写出 CLI 和 Web 环境的区别:

任何环境下,PHP 都有两个初始化过程和对应的两个终止过程。

我们要明白,Web 环境(CGI)下,PHP 首先被 Apache、Nginx、IIS 之类的 Web 代理程序作为模块加载并初始化,此时 PHP 被初始化的部分是常驻内存之中的,当一个 HTTP Request 过来时,Web 代理会通知 PHP 再一次创建一个 Runtime,这时候才是执行我们的 PHP Web 程序的代码,此时作为 Web 程序可以接受来自前端(如 Apache、Nginx、IIS)提供的服务器变量如 $_SERVER$_POST 等等。

那么对于 CLI 而言,由于没有前端代理,其本身启动过程就已经包含了两个初始化过程和终止过程,这意味着 CLI 可以实现比 CGI 更多功能,包括 CGI 的 HTTP 服务(利用诸如 Swoole 扩展和 Workerman 框架或自行利用 socket 实现)。

于是我们说两者的区别在于所利用的环境变量不同,但本质区别在于初始化过程和外部接口的变化。

这么说来,实际上不用依赖 web 服务端程序 PHP 也肯定能独立运行,自行构建一个 web 服务端(利用 Socket 相关的函数、类)然后自行解析协议文本和数据即可。当然我相信很多 phper 肯定还不会用 Socket,不过这不是本文讨论的主题。如果真的感兴趣,可以了解 Walkerman 这个框架 (http://www.workerman.net),当然还有 Swoole 扩展(http://www.swoole.com)。

CLI 程序获取外部变量的方式

CLI 程序获取外部变量的方式很简单,只有两个 $argv$argc,前者是获取命令行参数,第二个是参数的个数。

假设我们有文件 console.php

<?php
printf("num: %s\n", $argc);

var_dump($argv);

我们执行 php console.php hi hello,输出:

num: 3
array(3) {
  [0] =>
  string(11) "console.php"
  [1] =>
  string(2) "hi"
  [2] =>
  string(5) "hello"
}

CLI 程序的意义

CLI 的出现使得 PHP 的实用价值再次得到了提高,使其不单单是为了 web 程序的开发。作为当今简单易学的编程语言,PHP 可以用在更多的场景之下。这对于后端开发有着重大意义,所以我们看到了当下最为易用的 Composer,这个 PHP 包管理器大大减少了因为依赖出现的问题,使得轮子更容易被利用、开发效率大大提高。

Composer 是一个典型的 CLI 程序,使用者会发现他有着常见命令行程序基本都有的功能,如命令格式、命令参数、命令设置选项和其相关的描述、帮助信息等等。

这样一个程序,也告诉了我们,PHP CLI 的价值所在。我们对于个人电脑中的一些常见的文件管理、数据库清理和初始化、一些本地日志操作等等,都可以写一个 PHP CLI 程序来帮助我们,PHP 本身的强大功能也远远超过 Shell 提供的能力。

很多人也许还不知道,PHP 也是可以写 GUI 的,这同样是因为 CLI。当然,我们知道执行效率是一个大问题,因此 PHP 在这一方面并不擅长。但不擅长不代表不可以。更多的东西依旧等待着去发现,