百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

PHP的CLI命令行运行模式浅析

bigegpt 2024-08-27 12:08 3 浏览

在做开发的时候,我们不仅仅只是做各种网站或者接口,也经常需要写一些命令行脚本用来处理一些后端的事务。比如对数据进行处理统计等。当然也是为了效率着想,当一个事务有可能会有较长的耗时时,往往会交由服务器的定时器来固定时间调用脚本进行处理,从而让客户端能够有更好的用户体验。我们今天就来了解下 PHP 的命令行运行模式,也就是 PHP CLI 。

CLI 与 CGI

首先来看一下 CLI 和 CGI 的区别。我们都知道,Nginx 使用的是 FastCgi 来调用 PHP 的服务。 CGI 是通用编程接口,也就是给调用者提供的一种使用本程序的接口。 Nginx 这种类型的服务器并不是直接运行 PHP 程序的,而是通过 FastCgi 来执行 PHP 程序并获得返回结果。

CLI 则是 Command Line Interface,即命令行接口。主要用作 PHP 的开发外壳应用。也就是用 PHP 来进行 shell 脚本的开发。相比 linux 原生的 shell 来说,当然是方便了许多。在命令行状态下,直接使用 php 命令就可以运行某段 PHP 代码或某个 PHP 文件了。

另外,我们在命令行也可以直接使用 phpcgi 来运行一段 PHP 代码或者某个 PHP 文件,它和直接使用 php 命令来运行有什么区别呢?

  • CLI 的输出没有任何头信息
  • CLI 在运行时,不会把工作目录改为脚本的当前目录
  • CLI 出错时输出纯文本的错误信息(非 HTML 格式)
  • 强制覆盖了 php.ini 中的某些设置,因为这些设置在外壳环境下是没有意义的
// PHP的CLI命令行运行模式浅析.php
echo getcwd();

//  php-cgi dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php
// ...../MyDoc/博客文章/dev-blog/php/202004/source

// php dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php
// ...../MyDoc/博客文章

我们选取最典型的一个例子,我们运行的这个文件中,使用 getcwd() 输出当前脚本运行的目录,可以看出两种运行方式输出的结果明显不同。php-cgi 是以文件所在目录为基准输出,而 php 则是以当前运行这个命令的目录为基准输出。

直接运行 PHP 代码

在做一些简单的调试的时候,我们可以直接通过 CLI 来运行一段代码。

// php -r "echo 121;"
// 121

也就是简单的加个 -r 参数,后面跟上一段代码,这段代码必须用引号括起来。而且这个引号更推荐使用单引号,后面的例子会展示为什么用单引号更好。

CLI 获取参数

命令行模式下也是可以给脚本传递参数的。

// PHP的CLI命令行运行模式浅析.php
print_r($argv);
// php-cgi dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php 1 2 3
// X-Powered-By: PHP/7.3.0
// Content-type: text/html; charset=UTF-8

// php dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php 1 2 3
// Array
// (
//     [0] => dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php
//     [1] => 1
//     [2] => 2
//     [3] => 3
// )

在测试文件中,我们打印了 \$argv 变量。PHP 脚本运行的时候,会将命令行的所有参数保存在 $argv 变量中,并且还有一个 $argc 变量会保存参数的个数。

我们依然是使用 php-cgi 和 php ,两种模式来测试,从这里我们能发现 php-cgi 模式中 $argv 打印的内容竟然是头信息,而不是具体的参数信息。这也没错,毕竟 CGI 模式本来就是为 Web 服务器提供的接口,所以它接收的是 post 、 get 这类的参数而不是命令行的参数。

CLI 模式下我们正常获得了参数内容,并且 $argv[0] 始终保存的是当前运行文件及路径。

CLI 命令行实用选项

最后,我们再介绍一些命令行中常用的选项。

-r 直接运行代码时的参数传递

// php -r "var_dump($argv);" app 
// Warning: var_dump() expects at least 1 parameter, 0 given in Command line code on line 1
// 双引号 ",sh/bash 实行了参数替换

// php -r 'var_dump($argv);' app
// array(2) {
//     [0]=>string(19) "Standard input code"
//     [1]=>string(3) "app"
// }

// php -r 'var_dump($argv);' -- -h
// array(2) {
//     [0]=>string(19) "Standard input code"
//     [1]=>string(2) "-h"
// }

第一段代码在对双引号运行的 CLI 代码进行参数传递的时候,会直接报警告。其实很好理解,双引号里面的$会让系统的 sh/bash 以为这是个变量从而进行变量参数替换。所以更推荐使用单引号进行日常的简单测试。

第二段代码能够正常打印传递进来的参数内容。第三行代码则是需要传递带 - 符号的内容时,需要先给一个 -- 参数列表分隔符。这是因为 -xxx 的内容会让 php 命令认为这是一个命令选项而不是参数,所以我们添加一个分隔符就可以让分隔符之后的参数内容原样传递进代码中。

交互式地运行 PHP

// php -a
// php > $a = 1;
// php > echo $a;
// php > 1

添加一个 -a 选项,PHP 就会以交互式地形式运行,我们可以直接在交互状态下写代码或运行任何内容。

查看 phpinfo() 及已经安装的模块

这两个应该是大家经常会使用的两个选项。

// 输出 phpinfo()
// php -i

// 输出 PHP 中加载的模块
// php -m

// 查看模块详细信息
// php --ri swoole 

另外我们还可以通过 --ri 模块名 这个命令来查看具体某个扩展模块的详细信息。比如这里我们可以查看到 swoole 扩展的版本及相关的配置信息。

查看某个文件

// 显示去除了注释和多余空白的源代码
// php -w dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php
// <?php
//  echo getcwd(); print_r($argv);

// 通过 linux 管道读取输入
// cat dev-blog/php/202004/source/PHP的CLI命令行运行模式浅析.php | php -r "print file_get_contents('php://stdin');"
// ......这个文件里面所有的内容

最后两个小技巧,一个是通过 -w 选项,我们可以打印这个 php 文件中所有非注释和换行的内容。可以看成是像前端的代码压缩一样的能力。我们这个测试文件中有非常多的注释,通过这个命令后我们打印出来的内容是去除掉所有注释和空白行的结果。

另一个是我们可以用 linux 管道的方式向 PHP CLI 发送数据。这里我们通过 cat 查看我们的测试文件然后通过管道发送给 PHP CLI,在脚本中使用 STDIN 来读取管道发送过来的内容完成了整个文件内容的打印。这里我们没进行任何过滤,所以打印的是整个文件里面的内容,大家可以运行这个命令来测试。

总结

其实命令行模式运行的时候还有很多的选项,这里我们只是选取了一部分非常有用的内容进行展示。当然,大部分框架都提供了用于命令行的脚本框架,比如 laravel 中可以通过 php artisan make:command 来创建命令行脚本,然后使用 php artisan 来运行框架中的脚本。这些内容将来我们在学习框架方面知识的内容将会进行详细的讲解。

命令行 CLI 模式的应用非常广泛,几乎任何项目中都会使用到,所以,深入的学习掌握它将会使我们大受裨益。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202004/source/PHP%E7%9A%84CLI%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%BF%90%E8%A1%8C%E6%A8%A1%E5%BC%8F%E6%B5%85%E6%9E%90.php

参考文档:

https://www.php.net/manual/zh/features.commandline.php

相关推荐

最全的MySQL总结,助你向阿里“开炮”(面试题+笔记+思维图)

前言作为一名编程人员,对MySQL一定不会陌生,尤其是互联网行业,对MySQL的使用是比较多的。对于求职者来说,MySQL又是面试中一定会问到的重点,很多人拥有大厂梦,却因为MySQL败下阵来。实际上...

Redis数据库从入门到精通(redis数据库设计)

目录一、常见的非关系型数据库NOSQL分类二、了解Redis三、Redis的单节点安装教程四、Redis的常用命令1、Help帮助命令2、SET命令3、过期命令4、查找键命令5、操作键命令6、GET命...

netcore 急速接入第三方登录,不看后悔

新年新气象,趁着新年的喜庆,肝了十来天,终于发了第一版,希望大家喜欢。如果有不喜欢看文字的童鞋,可以直接看下面的地址体验一下:https://oauthlogin.net/前言此次带来得这个小项目是...

精选 30 个 C++ 面试题(含解析)(c++面试题和答案汇总)

大家好,我是柠檬哥,专注编程知识分享。欢迎关注@程序员柠檬橙,编程路上不迷路,私信发送以下关键字获取编程资源:发送1024打包下载10个G编程资源学习资料发送001获取阿里大神LeetCode...

Oracle 12c系列(一)|多租户容器数据库

作者杨禹航出品沃趣技术Oracle12.1发布至今已有多年,但国内Oracle12C的用户并不多,随着12.2在去年的发布,选择安装Oracle12c的客户量明显增加,在接下来的几年中,Or...

flutter系列之:UI layout简介(flutter-ui-nice)

简介对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了。布局的英文名叫做layout,就是用来描述如何将组件进行摆放的一个约束。在flutter中,基本上所有的对象都是wi...

Flutter 分页功能表格控件(flutter 列表)

老孟导读:前2天有读者问到是否有带分页功能的表格控件,今天分页功能的表格控件详细解析来来。PaginatedDataTablePaginatedDataTable是一个带分页功能的DataTable,...

Flutter | 使用BottomNavigationBar快速构建底部导航

平时我们在使用app时经常会看到底部导航栏,而在flutter中它的实现也较为简单.需要用到的组件:BottomNavigationBar导航栏的主体BottomNavigationBarI...

Android中的数据库和本地存储在Flutter中是怎样实现的

如何使用SharedPreferences?在Android中,你可以使用SharedPreferencesAPI来存储少量的键值对。在Flutter中,使用Shared_Pref...

Flet,一个Flutter应用的实用Python库!

▼Flet:用Python轻松构建跨平台应用!在纷繁复杂的Python框架中,Flet宛如一缕清风,为开发者带来极致的跨平台应用开发体验。它用最简单的Python代码,帮你实现移动端、桌面端...

flutter系列之:做一个图像滤镜(flutter photo)

简介很多时候,我们需要一些特效功能,比如给图片做个滤镜什么的,如果是h5页面,那么我们可以很容易的通过css滤镜来实现这个功能。那么如果在flutter中,如果要实现这样的滤镜功能应该怎么处理呢?一起...

flutter软件开发笔记20-flutter web开发

flutterweb开发优势比较多,采用统一的语言,就能开发不同类型的软件,在web开发中,特别是后台式软件中,相比传统的html5开发,更高效,有点像c++编程的方式,把web设计出来了。一...

Flutter实战-请求封装(五)之设置抓包Proxy

用了两年的flutter,有了一些心得,不虚头巴脑,只求实战有用,以供学习或使用flutter的小伙伴参考,学习尚浅,如有不正确的地方还望各路大神指正,以免误人子弟,在此拜谢~(原创不易,转发请标注来...

为什么不在 Flutter 中使用全局变量来管理状态

我相信没有人用全局变量来管理Flutter应用程序的状态。毫无疑问,我们的Flutter应用程序需要状态管理包或Flutter的基本小部件(例如InheritedWidget或St...

Flutter 攻略(Dart基本数据类型,变量 整理 2)

代码运行从main方法开始voidmain(){print("hellodart");}变量与常量var声明变量未初始化变量为nullvarc;//未初始化print(c)...