pfaciana / wp-routines
WordPress 中运行例程的浏览器端控制台
README
WP Routines 是一个WordPress的浏览器端控制台,可以显示执行代码的命令行风格输出。
它将类似命令行的交互方式带到了浏览器中,但输入不再是文本命令,而是来自HTTP请求。目的是提供服务器上执行代码的实时更新。通常,这用于管理任务或计划任务。可能需要几分钟才能执行的代码,并且实时更新对于进度和日志记录很有帮助。此外,它还可以进行交互,因为它只是一个HTML输出,可以具有可点击的事件。
WP Routines 可以与 WP CLI 具有类似的使用案例,但有两个主要区别。一是你不需要学习或编写特定于 CLI 的代码。二,更重要的是,你可能无法在特定服务器上访问 CLI。然而,如果这两个问题与你的使用案例相关,WP CLI 通常是一个更好的选择。
入门指南
作为 composer 包安装
composer require pfaciana/wp-routines
注意:这也包含在 WP Debug Bar 中,这也是一个 composer 包。
composer require pfaciana/wp-debug-bar
如果你已经在使用 WP Debug Bar 或想使用它,那么你不需要通过 composer 安装 WP Routines。
如何使用
概念
有三个主要概念:一个 Stream
、一个 Task
和一个 Page
,Routines
单例管理所有这些。
简单来说,Routines
是一组 Page
的集合。一个 Page
是一组 Task
的集合。一个 Task
可以是任何接受一个 Stream
参数的已注册函数。一个 Stream
实例允许开发者将内容发送到浏览器。
文档链接
- Routines - 一组
Page
- Page - 一组任务组或
Tasks
- Tasks - 这是一个抽象类。当扩展时,代表一个
Page
上的Task
组 - Task - 一个包含要在
Page
上执行的回调方法的类 - Stream - 与ajax调用进行通信的层
请参阅 Wiki 获取完整文档。
在最简单的形式中...
add_action( 'wp_routines', function ( \WP_Routines\Routines $routines ) { $routines->addTask( 'My Task Title', function ( \WP_Routines\Stream $stream ) { $stream->send( 'Hello World!' ); } ); });
就是这样!这将自动创建一个名为 Routines Console
的WordPress管理页面(默认名称)。在该页面上,将显示控制台HTML和顶部可用的任务组标题。在这个例子中,将只有一个任务,称为 My Task Title
。当用户单击 My Task Title
时,将向服务器发出ajax调用,并返回的数据流将实时打印 Hello World!
到控制台。现在,WP Routines 比这更强大,但这是最基本的概念。
其他示例
创建一个任务
add_action( 'wp_routines', function ( \WP_Routines\Routines $routines ) { // This is the long style first, then we'll go over shorthand options. // $taskCallback can be any function that accepts a $stream as the first argument $taskCallback = function ( \WP_Routines\Stream $stream ) { $stream->send( 'Begin...' ); $result = do_some_complicated_thing(); echo $result; $stream->flush(); }; // $pageConfig can be a config array or a `Page` object, here we'll define the config // if the `Page` already exists, then this can be the string representing the $menu_slug $pageConfig = [ 'menu_slug' => 'page_name_123', 'menu_title' => 'Page 123', 'page_title' => 'Page 123 Console', 'capability' => 'manage_options', 'icon_url' => 'dashicons-tickets', 'groups' => [ 'Group Name #2', 'default' => 'Group Name' ], 'admin_menu_page' => TRUE, 'debug_bar_panel' => TRUE, ]; $taskConfig = [ 'title' => 'Tab Name', 'group' => 'Group Name', 'page' => $pageConfig, 'callback' => $taskCallback, 'priority' => 10, ]; // Register the Task to the Routines manager via the config array $task = $routines->addTask( $taskConfig ); // Or as a `Task` object $task = $routines->addTask( new \WP_Routines\Task( $taskConfig ) ); # Here are a couple quick shorthand options // 1) Send just a callable, the rest of the values will be auto generated $routines->addTask( 'some_function' ); // 2) Or send the title and callback, and the rest of the values will be auto generated $routines->addTask( 'Tab Title', 'some_function' ); // You can also add a group of tasks all at once by adding a `Tasks` class $routines->addTasks( new Custom_Tasks_Class() ); // More on this later... });
有关默认值的说明
- 默认的
title
是 '任务 #1',其中数字会随着添加额外任务而递增- 默认的
group
是 '主组'- 默认的
page
是自动生成的 '常规控制台' 管理页面- 默认的
priority
是 10
创建页面
默认情况下,您无需创建页面,将为您自动创建一个名为 '常规控制台' 的管理页面。但是,您可以覆盖此设置,或使用 Page
类创建额外的页面。
add_action( 'wp_routines', function ( \WP_Routines\Routines $routines ) { // Create the page config array // You should notice most of these keys match the arguments for the `add_menu_page` and `add_submenu_page` functions // That's no coincidence, depending on if you add a `parent_slug` key, it will call once of those function using these values $pageConfig = [ 'menu_slug' => 'page_name_123', 'menu_title' => 'Page 123', 'page_title' => 'Page 123 Console', 'capability' => 'manage_options', 'icon_url' => 'dashicons-tickets', 'groups' => [ 'Group Name #2', 'default' => 'Group Name' ], 'admin_menu_page' => TRUE, 'debug_bar_panel' => TRUE, ]; // Register the Page to the Routines manager via the config array $page = $routines->addPage( $pageConfig ); // Or as a `Page` object $page = $routines->addPage( new \WP_Routines\Page( $pageConfig ) ); // At a bare minimum you can just send the $pageConfig['menu_slug'] $page = $routines->addPage( 'page_name_123' ); // The rest of the config array will be built with the default values });
将任务或多个任务添加到页面
将任务或多个任务添加到页面的操作与将其添加到上面的 $routines 管理器相同。唯一的区别是,由于您正在将其添加到现有页面,因此将使用该页面而不是默认或配置中定义的页面。
在 Task
或 Tasks
对象中定义 $page 并通过 $routines 管理器注册与不定义 Task
或 Tasks
中的 $page,但通过 $page 本身注册相同。两种方法都有效且效果相同。
add_action( 'wp_routines', function ( \WP_Routines\Routines $routines ) { $page = $routines->addPage( $pageConfig ); $page->addTask( 'Tab Title', 'some_function' ); $page->addTasks( new Custom_Tasks_Class() ); });
创建 Tasks
有关完整的 $config 选项,请参阅 Tasks 文档,但以下是一些需要注意的事项。
$this->taskPrefix
- 是一个方法应该具有的前缀,以便将其注册为任务。默认:'wp_ajax_'。所有以这个字符串开头(并且是公共方法)的方法将自动成为 Task
$this->crons
- 是一个要注册为计划任务的数组[]。第一级键是注册到 WordPress 的计划任务 ID。如果您想使用自定义的计划任务,必须首先通过挂钩到 cron_schedules
过滤器钩子(见 cron_schedules 钩子文档)来创建它。您可以在可选的 $this->preInit()
方法中这样做(见下面的示例)。在第二级,crons 数组使用键作为方法的名称,值是运行该方法的计划任务优先级。如果优先级值是一个数组,则将安排多个计划任务以匹配这些优先级。您可能希望为在运行其他代码之前和之后运行的清理方法使用它。
class Custom_Tasks_Class extends \WP_Routines\Tasks { // Set the page (Optional). If this is undefined, then it will go on the default page protected $page = 'page_name_123'; // Optional Crons setup protected $crons = [ 'hourly' => [ // <- schedule name 'wp_ajax_and_cron_task' => 10, // <- method name & priority ], 'custom_schedules_name' => [ // <- schedule name 'just_a_cron_task' => [-999, 999], // <- method name & priorities ], ]; protected function preInit ( $config ) { add_filter('cron_schedules', [$this, 'add_custom_cron_intervals'], 10, 1); } public function add_custom_cron_intervals ( $schedules ) { $schedules['custom_schedules_name'] = [ 'interval' => 15 * MINUTE_IN_SECONDS, 'display' => 'Once Every 15 Minutes', ]; return $schedules; } // Methods protected function neither_ajax_or_cron () { // This does not start with $taskPrefix, and is not in the $crons array } public function wp_ajax_import_data ( \WP_Routines\Stream $stream ) { // Starts with $taskPrefix, but not in the $crons array $stream->send('I only run as a task from the admin page.') } public function wp_ajax_and_cron_task ( \WP_Routines\Stream $stream ) { // Starts with $taskPrefix AND is in the $crons (hourly) $this->neither_ajax_or_cron(); $stream->send('I run both as a task and as a cron job.') } public function just_a_cron_task ( \WP_Routines\Stream $stream ) { // Does not start with $taskPrefix, but is in the $crons (custom_schedules_name) $this->neither_ajax_or_cron(); $stream->send('I only run as a cron job.') } } // If you're not autoloading or adding to a page, then you must add the new instance to the $routines manager manually add_action( 'wp_routines', function ( \WP_Routines\Routines $routines ) { $routines->addTasks( new Custom_Tasks_Class() ); }); // Bare minimum setup class Bare_Minimum_Tasks_Class extends \WP_Routines\Tasks { public function wp_ajax_ ( $stream ) { $stream->send( 'This is all you need to get this to work!' ); } };
匿名类
您还可以使用匿名类来构建 Page
、Tasks
或 Task
。只要它在 composer 自动加载文件加载后并在 admin_menu 创建之前,它们可以放在代码的任何位置。以下是开始时的非常简单的设置示例。以上和文档中的一切仍然适用。
对于 Page
,您需要一个 $this->config['menu_slug']
和 $this->config['autoload'] = TRUE
new class() extends \WP_Routines\Page { protected $config = [ 'menu_slug' => 'some_page_123', 'autoload' => TRUE, ]; };
对于 Tasks
,您至少需要一个以 $this->taskPrefix
开头的公共方法
new class() extends \WP_Routines\Tasks { public function wp_ajax_some_callable ( \WP_Routines\Stream $stream ) { $stream->send( 'Hello World!' ); } };
对于 Task
,您需要一个名为 render
的公共方法
new class() extends \WP_Routines\Task { public function render ( \WP_Routines\Stream $stream ) { $stream->send( 'Hello World!' ); } };
Stream
类
从 Page
创建一个新的 Stream
类实例(见 Stream 文档)并将其发送到它所在的活跃任务。
注意:有一组实用的过滤器钩子,允许您自定义
Stream
的功能(见 钩子文档)。
有几种方法可用,但我们将在这里介绍最重要的几种。
function render ( \WP_Routines\Stream $stream ) { // Sending text (or html) to the client/browser console. $stream->send("<b>Header</b>\n---"); ( function () { echo implode("\n", ['Row 1', 'Row 2', 'Row 3']; } )(); $stream->flush(); // `send` and `flush` are very similar. The difference being that `send` accepts text (or html) and `flush` does not. // `flush` calls `send`, with the text/html argument being the flushed buffer. // If nothing is in the buffer, then the output text/html is an empty string. // You typically would use `flush` when some code, which is out of scope of the `$stream` variable, // sends something to the buffer, and you want that to be output in real time. // A couple helpful methods that allow you track the status of a script. // These values can be conditions that trigger an action or lack of an action. // For example, if you are running out of execution time, you may want to gracefully end a script early // or if you are close to max memory, it could be a sign of a memory leak or bad code, etc. $currentAllocatedMemoryInMB = (int) $stream->getMemory(); $secondsRemainingBeforeScriptTimesout = (int) $stream->getTimeRemaining(); }
Stream
魔法字符
目前有两种任意字符可以以特定方式操作缓冲区输出。
这些字符是 \a
和 \b
。如果您将这些字符发送到缓冲区,浏览器控制台会将其解释为指令。这类似于 \n
是换行符。 \a
告诉控制台将位置移动到上一行的开头,删除当前行和上一行的所有内容。 \b
告诉控制台将位置移动到上一行的末尾,删除当前行上的所有内容。通常当前行是空的,所以 \b
通常不会删除任何输出,只是将最后的位置向后移动一个位置。
示例
$stream->send( 'Line 1!' ); $stream->send( 'Line 2!' ); $stream->send( 'Line 3!' ); $stream->send( "\a\a\b" ); $stream->send( "New Line 2!" );
输出...
# After First `send`
Line 1!
# After Second `send` Line 1! Line 2!
# After Third `send` Line 1! Line 2! Line 3!
# After Fourth `send` Line 1! # Explanation // the first `\a` deletes 'Line 3!' and puts the position at the beginning of that line // the second `\a` deletes 'Line 2!' and puts the position at the beginning of that line // the `\b` puts the position to the end of the 'Line 1!' line // and the `send()` itself puts the position on the next line when complete
# After Last `send` Line 1! New Line 2!
注意:另一种实现相同功能的方法是将第4次和第5次发送组合为
$stream->send( "\a\aNew Line 2!" );
然而,我想将步骤分解成单独的部分以便于解释。
示例 2 - 旋转星号
for ( $i = 1; $i <= 5; $i++ ) { $stream->send( "\a-" ); usleep( 100000 ); $stream->send( "\a\\" ); usleep( 100000 ); $stream->send( "\a|" ); usleep( 100000 ); $stream->send( "\a/" ); usleep( 100000 ); }
输出...
- (pause) \ (pause) | (pause) / (pause) x5
示例 3 - 文本进度条
echo "\n\n\n"; for ( $i = 1; $i <= 100; $i++ ) { $stream->send( "\a\a{$i}%" ); $stream->send( '[', 0 ); for ( $j = 1; $j <= 100; $j++ ) { $stream->send( $j < $i || $i == 100 ? "=" : ( $j == $i ? '>' : " " ), 0 ); } $stream->send( ']' ); }
输出...
90%
[=========================================================================================> ]
示例 4 - HTML 进度条
echo "\n\n\n"; for ( $i = 1; $i <= 100; $i++ ) { $stream->send( "\a\a{$i}%" ); $stream->send( ( "<div style='height: 20px; width: 100%; background-color: white;'><div style='height: 20px; width: {$i}%; background-color: green;'></div></div>" ) ); }
输出...
90% <div style="height: 20px; width: 100%; background-color: white;"> <div style="height: 20px; width: 90%; background-color: green;"></div> </div>
灵活性
我投入了大量精力提供多种执行相同任务的方式,根据您的编码偏好和具体情况进行调整,以使开发尽可能快速。您可以使用 $routines
单例管理器,并在此基础上构建一切,您也可以创建类的单独实例并将它们自动加载到 $routines
管理器中,或者创建匿名类。
我还允许使用大多数事物的简写形式。您可以通过字符串、配置数组或特定的实例来引用 Page
、Tasks
或 Task
。代码将确定如何获取它所需的内容。对于 Page
,字符串表示是 $this->config['menu_slug']
,对于 Tasks
是其 $this->group
,对于 Task
是其 $this->title
。