由于此处文档的原因,无法正确解析快捷代码

时间:2016-07-30 作者:Leo Jiang

我有一篇类似这样的帖子:

[code]
$foo = <<<EOT
  ....
EOT;
[/code]
它通过以下方式转换为此do_shortcodes_in_html_tags():

[code]
$foo = <<<EOT
  ....
EOT;
&#91;/code&#q3;
因此,短代码不会运行。有没有办法让这个短代码正常工作?

2 个回复
SO网友:birgire

解决方法可能是:

[code]
$foo = &lt;&lt;&lt;EOT
  ....
EOT;
[/code]
我们改变的地方<<<&lt;&lt;&lt;. 我们可以在之前自动完成do_shortcode 过滤内容,然后再次替换。

我测试了这个版本:

[code]
$foo = <<<EOT
....
EOT;<!---->
[/code]
而且它似乎能正确解析shortocde的内容。但是我们需要去掉额外的<!----> 零件,之后do_shortcode 已筛选内容这种带有HTML注释的方法将显示正确的HTML源,但渲染会有问题,除非在<textarea>.

Peeking into the /wp-includes/shortcodes.php file

在短代码的内容中使用未关闭的HTML标记的问题似乎是do_shortcodes_in_html_tags() 解析器认为[/code] 是它的一部分。

This part 属于do_shortcodes_in_html_tags() 似乎影响了这种行为:

// Looks like we found some crazy unfiltered HTML.  Skipping it for sanity.
$element = strtr( $element, $trans );
在哪里[/code] 替换为&#91;/code&#93;.

这发生在短代码regex替换之前。

我们会看到&#91;/code&#93; 从的输出the_content(), 如果this part:

$content = unescape_invalid_shortcodes( $content );
不会在do_shortcode() 作用

所以好像我们没有关闭短代码。

HTML Encoding The Code Blocks

我们可以使用HTML对代码块的内容进行编码:

add_filter( \'the_content\', function( $content )
{
    if( has_shortcode( $content, \'code\' ) ) 
        $content = preg_replace_callback( 
            \'#\\[code\\](.*?)\\[/code\\]#s\', 
            \'wpse_code_filter\', 
            $content 
        );

    return $content;

}, 1 );
其中,自定义回调定义为:

function wpse_code_filter( $matches )
{
    return esc_html( $matches[0] );
}
请注意,仍有一些事情需要整理,如

核心内的内容过滤器,例如wpautop 过滤器,来自第三方插件的内容过滤器,代码块内的短代码,等等SyntaxHighligther Evolved 看看需要什么样的解决办法。(我和那个插件没有关系)。

Alternatives

另一种方法是将代码移到内容编辑器之外,并将其存储在。

自定义字段、自定义表格、自定义帖子类型(例如,存储为摘录)

add_shortcode( \'codeblock\', function( Array $atts, String $content )
{
    // Setup default attributes
    $atts = shortcode_atts( [ \'id\' => 0 ], $atts, \'codeblock_shortcode\' );

    // We only want to target the \'codeblock\' post type
    if( empty( $atts[\'id\'] ) || \'codeblock\' !== get_post_type( $atts[\'id\'] ) ) 
        return $content;

    // Pre wrapped and HTML escaped post-excerpt from the \'codeblock\' post type
    return sprintf( 
            \'<pre>%s</pre>\',
            esc_html( 
                get_post_field(
                    \'post_excerpt\', 
                    $atts[\'id\'], 
                    \'raw\' 
                )
            )
        );
} );
这可能需要进一步的测试和调整

然后我们可以使用

[codeblock id="123"]
在内容编辑器中。

SO网友:birgire

另一种方法是通过临时删除短代码来预处理短代码的内容,例如在wpautop()do_shortcode() 对其进行操作,然后再次更换。

我相信这种方法以前已经以各种方式实现过,但我是这样玩的:

示例以下是预处理的示例:

[code lang="php"]
$foo = <<<EOT
  ....
EOT;
[/code]
此代码块将替换为:

[code lang="php"]code_b6a7d410d48883693b6714b68a4eee0a[/code]
参考号可以是md5 代码块的内容。

处理do_shortcode()wpautop() 现在代码块的内容应该不会有问题了。

然后,我们的快捷码将输出与参考号对应的代码块。

基本实现

基本设置将注册短代码和早期预处理过滤器:

add_shortcode( $tag, [ $this, \'shortcode\' ] );
add_filter( \'the_content\', [ $this, \'filter_before\' ], 0 );
短代码定义为:

public function shortcode( $atts, $content )
{
    // Default attributes
    $atts = shortcode_atts( [\'lang\' => \'php\'], $atts, \'codeblock_shortcode\' );

    // Restore the code block\'s content, HTML escaped it and pre-wrap it
    return sprintf( 
        \'<pre lang="%s">%s</pre>\',
        esc_attr( $atts[\'lang\'] ),
        esc_html( $this->filter_after( $content ) ) 
    );
}
其中代码块隐藏在filter_before() 方法,并使用filter_after() 方法然后HTML转义并最终包装在<pre> 标签

在这种问题中,使用它很方便preg_replace 通过回调。

演示插件这里有一个演示插件:

<?php
/**
 * Plugin Name: Simple Code Block
 * Description: Support for the [code] shortcode to display code snippets.
 * Plugin URI:  http://wordpress.stackexchange.com/a/233740/26350
 * Author:      birgire
 * Version:     1.0.0
 */

namespace WPSE\\SimpleCodeBlock;

add_action( \'init\', function()
{
    $o = new CodeBlock;
    $o->init( $tag = \'code\' );
} );


class CodeBlock
{
    private $blocks;
    private $tag;

    public function init( $tag )
    {           
        if( ! shortcode_exists( $tag ) && preg_match( \'#^[a-z]+$#\', $tag ) )
        {
            $this->tag = $tag;
            $this->blocks = [];

            add_shortcode( $tag, [ $this, \'shortcode\' ] );
            add_filter( \'the_content\', [ $this, \'filter_before\' ], 0 );
        }
    }

    public function filter_before( $content )
    {
        if( $this->has_code_block( $content ) )
        {
            $tag = preg_quote( $this->tag );

            $pattern = "#(\\[{$tag}(\\s+[^\\]]+)?\\])(.*?)(\\[\\/{$tag}\\])#s";
            $content = preg_replace_callback( 
                $pattern, 
                [ $this, \'regex_before\' ], 
                $content 
            );

        }
        return $content;
    }

    public function filter_after( $content ) 
    {
        if( $this->has_code_key( $content ) )
        {
            $tag = preg_quote( $this->tag );
            $pattern = "#{$tag}\\_[a-z0-9]{32}#"; 
            $content = preg_replace_callback(
                $pattern,
                [ $this, \'regex_after\' ], 
                $content 
            );
        }       
        return $content;
    }

    public function regex_before ( Array $matches )  
    {               
        // Nothing to do 
        if( 5 !== count( $matches ) )
            return;

        // Generate a key from the code block\'s content
        $key = $this->tag . \'_\' . md5( $matches[3] );

        // Temporarily store the code block
        $this->blocks[$key] = $matches[3];  

        // Return this form [code ...]code_b6a7d410d48883693b6714b68a4eee0a[/code]
        return $matches[1] . $key . $matches[4];
    }

    public function regex_after( Array $matches )
    {   
        $key = $matches[0];

        // Restore the code block\'s content
        if( isset( $this->blocks[$key] ) )
            return $this->blocks[$key];

        return;     
    }

    public function shortcode( $atts = [], $content = \'\' )
    {
        // Default attributes
        $atts = shortcode_atts( [\'lang\' => \'php\'], $atts, \'codeblock_shortcode\' );

        // Restore the code block\'s content, HTML escaped it and pre-wrap it
        return sprintf( 
            \'<pre lang="%s">%s</pre>\',
            esc_attr( $atts[\'lang\'] ),
            esc_html( $this->filter_after( $content ) ) 
        );
    }

    private function has_code_key( $content )
    {
        return false !== strpos( $content, $this->tag . \'_\' );
    }

    private function has_code_block( $content )
    {
        return false !== strpos( $content, \'[\' . $this->tag );
    }

} // end class
也可以稍微调整一下,这样[code] 未定义为短代码,并以以下优先级还原代码块的内容PHP_INT_MAXthe_content 滤器

相关推荐

SHORTCODE_ATTS()中的$ATTS参数是什么?

这个WordPress developers reference page for shortcode_atts() 国家:$atts(array)(必选)用户在shortcode标记中定义的属性。但我不理解这个定义。例如,在WP Frontend Profile 插件:$atts = shortcode_atts( [ \'role\' => \'\', ], $atts ); 据我所知,shortcode\