pfaciana/wp-routines

WordPress 中运行例程的浏览器端控制台

1.0.0 2023-01-08 02:44 UTC

This package is auto-updated.

Last update: 2024-09-08 06:19:04 UTC


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 和一个 PageRoutines 单例管理所有这些。

简单来说,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 管理器相同。唯一的区别是,由于您正在将其添加到现有页面,因此将使用该页面而不是默认或配置中定义的页面。

TaskTasks 对象中定义 $page 并通过 $routines 管理器注册与不定义 TaskTasks 中的 $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!' );
    }
};

匿名类

您还可以使用匿名类来构建 PageTasksTask。只要它在 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}&percnt;" );
    $stream->send( '[', 0 );
    for ( $j = 1; $j <= 100; $j++ ) {
        $stream->send( $j < $i || $i == 100 ? "=" : ( $j == $i ? '>' : "&nbsp;" ), 0 );
    }
    $stream->send( ']' );
}

输出...

90%
[=========================================================================================>          ]
示例 4 - HTML 进度条
echo "\n\n\n";
for ( $i = 1; $i <= 100; $i++ ) {
    $stream->send( "\a\a{$i}&percnt;" );
    $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 管理器中,或者创建匿名类。

我还允许使用大多数事物的简写形式。您可以通过字符串、配置数组或特定的实例来引用 PageTasksTask。代码将确定如何获取它所需的内容。对于 Page,字符串表示是 $this->config['menu_slug'],对于 Tasks 是其 $this->group,对于 Task 是其 $this->title