建站教程

建站教程

Products

当前位置:首页 > 建站教程 >

PHP 和 WordPress 上的 Pygments

GG网络技术分享 2025-03-18 16:08 0


我一直在努力寻找一个很棒的代码荧光笔,我一直在使用很多我什至不记得的东西。 这些是我现在能记住的:

  • 语法高亮
  • 谷歌美化器
  • 荧光笔.js
  • 格氏

现在我正在使用 highlighter.js 但这并不是我想要的,我想要的是能够突出显示大多数“单词”或保留字,例如内置函数、对象等,而这个荧光笔和其中的大多数都缺少。 我知道这不是一件重要的事情,不幸的是,这件事一直停留在我的脑海中,直到现在。

最后,我找到了 Pygments 与我一直在寻找的完美匹配,它与 GitHub 使用的相同。 现在唯一的障碍是它是一个基于 python 的语法荧光笔,我使用的是 WordPress,而 Wordpress 是基于 PHP 构建的。

安装

但是,嘿,我们可以克服它,有一个解决方案,首先,我们需要在我们的服务器上安装 python,以便我们可以使用 Pygments。

我们不会对安装进行深入探讨,因为有这么多 OS 风格,而且每种风格可能略有不同。

Python

首先,您必须通过键入检查是否已经安装了 python python 在你的命令行上。

如果没有安装,您应该查看 Python 下载页面并下载您的操作系统安装程序。

PIP 安装程序

安装 点安装程序 根据其网站,有两种安装方法:

第一种也是推荐的方法是下载 get-pip.py 并在命令行上运行它:

python get-pip.py

第二种方法是使用包管理器,通过运行这两个可能的命令之一,就像之前提到的那样,这取决于您的服务器操作系统。

sudo apt-get install python-pip

或者:

sudo yum install python-pip

笔记: 你可以使用任何你喜欢的包管理器,比如easy_install,作为例子,因为它是我使用pip的Pygments网站上使用的包管理器。

Pygments

要安装 pygments,您需要运行以下命令:

pip install Pygments

如果您在用户没有 root 访问权限的服务器上,您将无法使用前面的命令安装它,如果是这种情况,您必须使用它运行它 --user 标志在用户目录上安装模块。

pip install --user Pygments

现在一切都安装好了,所以我们要做的就是使用 PHP 和一些 Python 代码

PHP + Python

它的工作方式是通过使用 php 执行 python 脚本 exec() 发送包含要突出显示的代码的文件的语言名称和文件名。

Python

我们要做的第一件事是创建 python 脚本,该脚本将使用 Pygments 将普通代码转换为突出显示的代码。

那么让我们一步一步来介绍如何创建python脚本。

首先我们导入所有需要的模块:

import sys

from pygments import highlight

from pygments.formatters import HtmlFormatter

sys 模块提供 argv 列表,其中包含传递给 python 脚本的所有参数。

highlight from pygments 实际上是主要功能以及 词法分析器 将生成突出显示的代码。 你会阅读更多关于 词法分析器 以下。

HtmlFormatter 是我们希望生成的代码的格式,我们将使用 HTML 格式。 以下是可用格式化程序的列表,以备不时之需。

# Get the code

language = (sys.argv[1]).lower()

filename = sys.argv[2]

f = open(filename, 'rb')

code = f.read()

f.close()

这段代码的作用是接受第二个参数 (sys.argv[1]) 并将其转换为小写文本,以确保它始终为小写。 因为 "php" !== "PHP". 第三个论据 sys.argv[2] 是代码的文件名路径,所以我们打开、读取其内容并关闭它。 第一个参数是 python 的脚本名称。

# Importing Lexers

# PHP

if language == 'php':

from pygments.lexers import PhpLexer

lexer = PhpLexer(startinline=True)

# GUESS

elif language == 'guess':

from pygments.lexers import guess_lexer

lexer = guess_lexer( code )

# GET BY NAME

else:

from pygments.lexers import get_lexer_by_name

lexer = get_lexer_by_name( language )

所以是时候导入词法分析器了,这段代码的作用是根据我们需要分析的语言创建一个词法分析器。 一个词法分析器它分析我们的代码并获取每个保留字、符号、内置函数等。

在这种情况下,在词法分析器分析后,所有代码将格式化为 HTML,将所有“单词”包装成一个带有类的 HTML 元素。 顺便说一句,类名根本不是描述性的,所以函数不是类“函数”,但无论如何这现在不用担心。

变量 language 包含我们要转换代码的语言名称的字符串,我们使用 lexer = get_lexer_by_name( language ) 用他们的名字来获得任何词法分析器,以及它自我解释的功能。 但是为什么我们检查 php猜测 首先你可能会问,好吧,我们检查 php,因为如果我们使用 get_lexer_by_name('php') 并且php代码没有所需的开口 php标签 不会像我们预期的那样很好地突出代码,我们需要像这样创建一个特定的 php 词法分析器 lexer = PhpLexer(startinline=True) 通过 startinline=True 作为参数,因此不再需要此打开的 php 标记。 guess 是我们从 php 传递给 pygments 的字符串,我们不知道它是哪种语言,或者没有提供语言,我们需要猜测它。

在他们的网站上有一个可用的词法分析器列表。

python 的最后一步是创建 HTML 格式化程序,执行突出显示并输出包含突出显示代码的 HTML 代码。

formatter = HtmlFormatter(linenos=False, encoding='utf-8', nowrap=True)

highlighted = highlight(code, lexer, formatter)

print highlighted

对于格式化程序,它已通过 linenos=False 不生成行号和 nowrap=True 不允许 div 包装生成代码。 这是个人决定,代码将使用 PHP 进行包装。

接下来就通过了 code 包含实际代码, lexer 包含语言词法分析器和 formatter 我们只是在上面的行中创建,它告诉突出显示我们希望我们的代码如何格式化。

最后输出代码。

这就是 python 的内容,即构建亮点的脚本。

这是完整的文件: 构建.py

import sys

from pygments import highlight

from pygments.formatters import HtmlFormatter

# If there isn't only 2 args something weird is going on

expecting = 2;

if ( len(sys.argv) != expecting + 1 ):

exit(128)

# Get the code

language = (sys.argv[1]).lower()

filename = sys.argv[2]

f = open(filename, 'rb')

code = f.read()

f.close()

# PHP

if language == 'php':

from pygments.lexers import PhpLexer

lexer = PhpLexer(startinline=True)

# GUESS

elif language == 'guess':

from pygments.lexers import guess_lexer

lexer = guess_lexer( code )

# GET BY NAME

else:

from pygments.lexers import get_lexer_by_name

lexer = get_lexer_by_name( language )

# OUTPUT

formatter = HtmlFormatter(linenos=False, encoding='utf-8', nowrap=True)

highlighted = highlight(code, lexer, formatter)

print highlighted

PHP - WordPress

让我们跳到 WordPress 并创建一个基本插件来处理需要突出显示的代码。

一辈子没为WordPress创建过插件也没关系,这个插件只是一个包含php函数的文件,没有WordPress插件开发知识也行,但你需要WordPress开发知识尽管。

在里面创建一个文件夹 wp-content/plugins 命名为 wp-pygments (可以是任何你想要的)并在里面复制 构建.py 我们刚刚创建的python脚本并创建一个名为的新php文件 wp-pygments.php (可能与目录同名)。

下面的代码只是让 WordPress 知道插件的名称和其他信息,此代码将位于顶部 wp-pygments.php.

<?php

/*

* Plugin Name: WP Pygments

* Plugin URI: http://wellingguzman.com/wp-pygments

* Description: A brief description of the Plugin.

* Version: 0.1

* Author: Welling Guzman

* Author URI: http://wellingguzman.com

* License: MEH

*/

?>

添加过滤器 the_content 寻找 <pre> 标签。 预期的代码是:

<pre class="php">

<code>

$name = "World";

echo "Hello, " . $name;

</code>

</pre>

笔记: html标签需要编码; 例如 < 需要是 &lt; 所以解析不会混淆并且做错了。

在哪里 class 是里面代码的语言 前置标签,如果没有类或为空将通过 guessbuild.py.

add_filter( 'the_content', 'mb_pygments_content_filter' );

function mb_pygments_content_filter( $content )

{

$content = preg_replace_callback('/

]?.*?>.*?<code>(.*?).*?/sim', 'mb_pygments_convert_code', $content);

return $content;

}

preg_replace_callback 函数将执行 mb_pygments_convert_code 每次使用提供的正则表达式模式匹配内容时的回调函数: /<pre(s?class="(.*?)")?[^>]?.*?>.*?<code>(.*?).*?/sim,它应该匹配任何

<代码> 在帖子/页面内容上。

关于什么 模拟?,这是三个模式修饰符标志。 来自 php.net:

  • s:如果设置了此修饰符,则模式中的点元字符匹配所有字符,包括换行符。
  • 一世: 如果设置了这个修饰符,模式中的字母匹配大小写字母。
  • :默认情况下,PCRE 将主题字符串视为由单个“行”字符组成(即使它实际上包含多个换行符)。

这可以通过 DOMDocument(); 也是。 代替 /<pre(s?class="(.*?)")?[^>]?.*?>.*?<code>(.*?).*?/sim 有了这个:

// This prevent throwing error

libxml_use_internal_errors(true);

// Get all pre from post content

$dom = new DOMDocument();

$dom->loadHTML($content);

$pres = $dom->getElementsByTagName('pre');

foreach ($pres as $pre) {

$class = $pre->attributes->getNamedItem('class')->nodeValue;

$code = $pre->nodeValue;

$args = array(

2 => $class, // Element at position [2] is the class

3 => $code // And element at position [2] is the code

);

// convert the code

$new_code = mb_pygments_convert_code($args);

// Replace the actual pre with the new one.

$new_pre = $dom->createDocumentFragment();

$new_pre->appendXML($new_code);

$pre->parentNode->replaceChild($new_pre, $pre);

}

// Save the HTML of the new code.

$content = $dom->saveHTML();

下面的代码来自 mb_pygments_convert_code 功能。

define( 'MB_WPP_BASE', dirname(__FILE__) );

function mb_pygments_convert_code( $matches )

{

$pygments_build = MB_WPP_BASE . '/build.py';

$source_code = isset($matches[3])?$matches[3]:'';

$class_name = isset($matches[2])?$matches[2]:'';

// Creates a temporary filename

$temp_file = tempnam(sys_get_temp_dir(), 'MB_Pygments_');

// Populate temporary file

$filehandle = fopen($temp_file, "w");

fwrite($filehandle, html_entity_decode($source_code, ENT_COMPAT, 'UTF-8') );

fclose($filehandle);

// Creates pygments command

$language = $class_name?$class_name:'guess';

$command = sprintf('python %s %s %s', $pygments_build, $language, $temp_file);

// Executes the command

$retVal = -1;

exec( $command, $output, $retVal );

unlink($temp_file);

// Returns Source Code

$format="<div class="highlight highlight-%s"><pre><code>%s</code></pre></div>";

if ( $retVal == 0 )

$source_code = implode("n", $output);

$highlighted_code = sprintf($format, $language, $source_code);

return $highlighted_code;

}

查看上面的代码:

define( 'MB_WPP_BASE', dirname(__FILE__) );

定义一个绝对插件的目录路径常量。

$pygments_build = MB_WPP_BASE . '/build.py';

$source_code = isset($matches[3])?$matches[3]:'';

$class_name = isset($matches[2])?$matches[2]:'';

$pygments_build 是python脚本所在的完整路径。 每次有一个匹配的数组称为 $matches 传递包含 4 个元素。 以此作为帖子/页面内容匹配代码的示例:

<pre class="php">

<code>

$name = "World";

echo "Hello, " . $name;

</code>

</pre>

  • 位置的元素 [0] 是整个

     匹配,其值为: 

    <pre class="php">

    <code>

    $name = "World";

    echo "Hello, " . $name;

    </code>

    </pre>

  • 位置的元素 [1] 是类属性名称及其值,其值为:

    class="php"

  • 位置的元素 [2] 是没有名字的类属性值,其值为:

    php

  • 位置的元素 [3] 是没有它的代码本身 pre 标签,其值为:

    $name = "World";

    echo "Hello, " . $name;

// Creates a temporary filename

$temp_file = tempnam(sys_get_temp_dir(), 'MB_Pygments_');

它创建一个临时文件,其中包含将传递给 python 脚本的代码。 这是处理代码的更好方法将被传递。 而不是将整个事情作为参数传递,这将是一团糟。

// Populate temporary file

$filehandle = fopen($temp_file, "wb");

fwrite($filehandle, html_entity_decode($source_code, ENT_COMPAT, 'UTF-8') );

fclose($filehandle);

它创建代码文件,但我们解码所有 HTML 实体,因此 pygments 可以正确转换它们。

// Creates pygments command

$language = $class_name?$class_name:'guess';

$command = sprintf('python %s %s %s', $pygments_build, $language, $temp_file);

它创建要使用的 python 命令,它输出:

python /path/to/build.py php /path/to/temp.file

// Executes the command

$retVal = -1;

exec( $command, $output, $retVal );

unlink($temp_file);

// Returns Source Code

$format="<div class="highlight highlight-%s"><pre><code>%s</code></pre></div>";

if ( $retVal == 0 )

$source_code = implode("n", $output);

$highlighted_code = sprintf($format, $language, $source_code);

执行刚刚创建的命令,如果返回 0,python 脚本上一切正常。 exec(); 返回 python 脚本的行输出数组。 所以我们将数组输出连接成一个字符串作为源代码。 如果没有,我们将坚持使用没有突出显示的代码。

通过缓存改进它

所以现在工作正常,但我们必须节省时间和处理,想象一下 100 <pre> 在内容上标记它会创建 100 个文件并调用 100 次 python 脚本,所以让我们缓存这个宝贝。

瞬态 API

WordPress 通过 Transient API 提供了在数据库中临时存储数据的能力。

首先,让我们添加一个动作 save_post 钩子,所以每次保存帖子时,我们都会转换代码并缓存它。

add_action( 'save_post', 'mb_pygments_save_post' );

function mb_pygments_save_post( $post_id )

{

if ( wp_is_post_revision( $post_id ) )

return;

$content = get_post_field( 'post_content', $post_id );

mb_pygments_content_filter( $content );

}

如果是修订版我们什么都不做,否则我们获取帖子内容并调用 pygments 内容过滤器函数。

让我们创建一些函数来处理缓存。

// Cache Functions

// Expiration time (1 month), let's clear cache every month.

define('MB_WPP_EXPIRATION', 60 * 60 * 24 * 30);

// This function it returns the name of a post cache.

function get_post_cache_transient()

{

global $post;

$post_id = $post->ID;

$transient="post_" . $post_id . '_content';

return $transient;

}

// This creates a post cache for a month,

// containing the new content with pygments

// and last time the post was updated.

function save_post_cache($content)

{

global $post;

$expiration = MB_WPP_EXPIRATION;

$value = array( 'content'=>$content, 'updated'=>$post->post_modified );

set_transient( get_post_cache_transient(), $value, $expiration );

}

// This returns a post cache

function get_post_cache()

{

$cached_post = get_transient( get_post_cache_transient() );

return $cached_post;

}

// Check if a post needs to be updated.

function post_cache_needs_update()

{

global $post;

$cached_post = get_post_cache();

if ( strtotime($post->post_modified) > strtotime($cached_post['updated']) )

return TRUE;

return FALSE;

}

// Delete a post cache.

function clear_post_cache()

{

delete_transient( get_post_cache_transient() );

}

在。。。之初 mb_pygments_content_filter() 添加一些行以检查是否有缓存的帖子。

function mb_pygments_content_filter( $content )

{

if ( FALSE !== ( $cached_post = get_post_cache() ) && !post_cache_needs_update() )

return $cached_post['content'];

clear_post_cache();

最后 mb_pygments_content_filter() 添加一行以保存帖子缓存。

save_post_cache( $content );

最后,当插件卸载时,我们需要删除我们创建的所有缓存,这有点棘手,所以我们使用 $wpdb 使用此查询删除所有对象。

register_uninstall_hook(__FILE__, 'mb_wp_pygments_uninstall');

function mb_wp_pygments_uninstall() {

global $wpdb;

$wpdb->query( "DELETE FROM `wp_options` WHERE option_name LIKE '_transient_post_%_content' " );

}

标签: WordPress 教程

提交需求或反馈

Demand feedback