1

我正在使用phpBB3 制作留言板。有一个内置功能可以获取帖子中的所有 URL,然后呈现为链接。我想做到这一点,以便只有本地链接可以点击。

phpbb3 在帖子的文本上使用正则表达式,并且对于每个匹配项将其更改为一个链接:

if ($somestuff){
// matches a xxxx://aaaaa.bbb.cccc. ...
$magic_url_match[] = '#(^|[\n\t (>.])(' . "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#ie';
$magic_url_replace[] = "make_clickable_callback(MAGIC_URL_FULL, '\$1', '\$2', '', '$class')";

// matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
$magic_url_match[] = '#(^|[\n\t (>])(' . "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#ie';
$magic_url_replace[] = "make_clickable_callback(MAGIC_URL_WWW, '\$1', '\$2', '', '$class')";
}
return preg_replace($magic_url_match, $magic_url_replace, $text);

如何重写这些正则表达式,使它们只匹配我域上的链接?此外,自学正则表达式的最佳方法是什么?

4

1 回答 1

2

这是第一个,逐段分解。即使这样做也不是微不足道的......

(
    ^
|
    [\n\t (>.]
)

好的,这里我们只是有“行的开头,或者在换行符,制表符,空格,大于,句点之后。只是锚定正则表达式。

(
    [a-z]$scheme*:/{2}

这是纯粹的精神错乱。$scheme大概成立http,这意味着这个正则表达式匹配http://. 为什么有人会使用/{2}而不是//,我无法开始猜测。

    (?:
        (?:
            [a-z0-9\-._~!$&'($inline*+,;=:@|]+
        |
            %[\dA-F]{2}
        )+
    |

这匹配一系列字符,大概是那些在 URL 中合法的字符。值得注意的是$inlinePHP 变量 - 无法猜测它包含什么 - 以及第二种选择,%[\dA-F]{2}. 这与%20空格等内容相匹配。该%符号在匹配(或 URL)中是不合法的。

同样重要的是,这/是不合法的。因此,这不能引用目录,只能引用域。这很可能是您想要更改的部分,以简单地匹配您网站的相应域。

不过,为了完整起见,这里是其余的。

        [0-9.]+
    |

或者,我们可以有一系列数字和句点——一个 IP 地址。考虑到这个正则表达式有多复杂,我很惊讶他没有去(?:\d{1,3}\.){3}\d{1,3}......

        \[
        [a-z0-9.]+
        :
        [a-z0-9.]+
        :
        [a-z0-9.:]+
        \]
    )

这是我们的最后一个选择;我认为这是针对 IPv6 的。无论如何,它是一系列用冒号分隔的十六进制数字。它要求这些在方括号内,我觉得这很奇怪,特别是对于一个如此大量使用这些标签的论坛软件......

    (?:
        :
        \d*
    )?

在这里,我们可以选择冒号后面的一些数字。也就是说,这适用于其中包含端口的 URL。

    (?:
        /
        (?:
            [a-z0-9\-._~!$&'($inline*+,;=:@|]+
        |
            %[\dA-F]{2}
        )*
    )*

好的,这里我们已经到了子目录,如/开头所示。否则,这是相同的“合法 URL 字符”匹配。

    (?:
        \?
        (?:
            [a-z0-9\-._~!$&'($inline*+,;=:@/?|]+
        |
            %[\dA-F]{2}
        )*
    )?
    (?:
        \#
        (?:
            [a-z0-9\-._~!$&'($inline*+,;=:@/?|]+
        |
            %[\dA-F]{2}
        )*
    )?
)

最后,正在传递的东西,由GET指示\?,以及链接到页面中间锚的 URL,由 . 指示\#

底线:

本节:

    [a-z]$scheme*:/{2}
    (?:
        (?:
            [a-z0-9\-._~!$&'($inline*+,;=:@|]+
        |
            %[\dA-F]{2}
        )+
    |
        [0-9.]+
    |
        \[
        [a-z0-9.]+
        :
        [a-z0-9.]+
        :
        [a-z0-9.:]+
        \]
    )

应该用这样的东西代替:

    [a-z]$scheme*://
    www\.example\.com

或许

    [a-z]$scheme*://
    (?:
        www\.example\.com
    |
        192\.168\.0\.1
    |
        ::ffff:192\.168\.0\.1
    )

域和 IP 地址与您的网站匹配的位置。显然,您将不得不删除我所做的换行符和缩进。我会为您做,但我认为这几乎不值得,因为您将很难找到将您的域置于所有这些中间的位置。

您可能希望为子域包含一些正则表达式,或者忽略www.您的内容。

您可能还想删除它:

    (?:
        :
        \d*
    )?

因为您可能不希望人们链接到您域上的其他端口。

第二个看起来具有大致相同的结构;正如评论所说,它只是获取缺少协议指示符的 URL。

于 2012-08-17T03:43:28.747 回答