【问题标题】:Converting string to <img> tag using preg_replace使用 preg_replace 将字符串转换为 <img> 标签
【发布时间】:2012-12-09 09:30:29
【问题描述】:

这些正则表达式正在杀死我,无论我读什么都无法理解这个概念。

这是我的问题,我相信它非常容易解决。

[img:http://example.com/_data/025_img.jpg]

我想要的只是用&lt;img&gt;标签改变它说[img:...]的位,并得到如下结果

<img src='http://example.com/_data/025_img.jpg' border='0' />

我尝试了各种愚蠢的变体,但都不起作用。我对此并不感到惊讶。

编辑

附加信息:

我的情况如下。

  1. 用户将图片上传到他们的个人资料

  2. 图像名称存储在数据库中。

  3. 它们列在具有文本区域的表单旁边
  4. 在输入文本时,我希望通过添加以下标签 [img: ... ] 为用户提供包含一个或多个图像的功能,其中 ... 是单击图像时将被复制的链接从用户库中列出。
  5. 我正在使用 Codeigniter 并将 textarea 通过视图传递到控制器->模型中,在该模型中,它由助手对各种事物进行清理... sql/quotes 等。XSS 也已启用关于CI

  6. 然后我想扫描文本并查看用户在哪里拥有 [img: ... ] 标签并将其交换为 &lt;img&gt;tag 并使用图像和文本呈现帖子。

所以用户的实际输入将类似于

The brown fox jumped over foo bar [img:http://example.com/_data/025_img.jpg] and then went to bed [img:http://example.com/_data/0277_img.jpg] while thinking about [img:http://example.com/_data/1115_img.jpg]

这就是我要求 preg_replace 而不是 preg_match 的原因。 preg_match 不会使文本跟随图像。

【问题讨论】:

  • 你想在这里完成什么?自定义标记语言?像 BBCode 之类的东西?您是否考虑过使用现有的标记语言和随附的库?
  • 基本上用户有一个图片库,我想让他们在以用户友好的方式键入文本的地方包含这些图片。
  • 好的。你进入了一个痛苦的世界。这可能需要一些时间来输入...
  • 我愿意接受有关如何完成此任务的任何建议。

标签: php preg-replace


【解决方案1】:

让我们先把简单的事情弄清楚。

/\[img:([^\]]+)\]/

即:

  • 文字[img:
  • 一个捕获组包含
    • 由以下组成的字符类
      • 不是文字的东西]
    • 至少重复一次
  • 文字]

通过preg_match 运行此程序,匹配数组中的元素 1 很可能是您可以轻松插入到img 标记中的图像 URL。

但你不应该。不是马上。

首先,这是不安全的。当我写这篇文章时会发生什么?

[img:javascript:alert(document.cookie);]

呃。这不会是好事。

可能想要确保用户声称是 URL 的东西确实是 URL。您可以致电parse_url 尝试执行此操作。它将返回一系列 URL 组件。确保事物具有域和路径,并通过 HTTP 或 HTTPS 提供服务。

好的,但是当用户输入 this 时会发生什么?

[img:http://www.example.com/foo.jpg" onmouseover="alert(document.cookie)"]

这是一个有效的...ish... URL,它将被parse_url 成功解构,并且很可能通过基本的格式检查。过滤掉空格和引号(单个 双引号)将是一个很好的起点,但还有更多需要担心的事情。

底线是这样的标记是XSS, or Cross-site scripting vulnerabilities 中的一个向量。

您可以可能通过htmlspecialchars 传递 URL 来减轻一些威胁。这至少会取消引号和括号,并且很难对那些被照顾的人感到讨厌。请注意字符集的愚蠢,一些非 UTF-8 字符编码可能包含 ASCII 引号......

可能想要为此使用真正的标记语言(即使它只是 markdown),并且您可能想要使用基于白名单的 HTML 过滤器,例如 @ 987654324@ 结果。这将有助于保护您免受某些程度的精神错乱。

记住,只有当他们出手抓你时,你才是偏执狂。网络上到处都是愚蠢到恶意的人,以及恶意到愚蠢的人。

【讨论】:

  • 我正在使用 CI 来清理来自用户的输入,所以基本上当他们提交文本字段时,它会通过一个安全帮助程序,然后我正在考虑尝试实现获得一部分的目标它有一些线索的文本,即 [img: ... ] 并将其转换为图像标签。 XSS 在 CI 配置中也已打开。这会改变现状吗?关于如何进行的任何建议?
  • 我对 CodeIgniter 的清理或 XSS 过滤一无所知,但我们假设它是不错的。尝试使用其中一些示例来打破它。尽你最大的努力摆脱标签并变得愚蠢。例如,[img:"&gt;&lt;b&gt;Boo!&lt;/b&gt;] 可能会直接通过,因为不涉及 javascript。也就是说,它也未通过 URL 测试。通过htmlspecialchars 传递整个事件也可能是足够的保护级别。 Lemme 实际上添加了...
  • 很好的答案,很高兴您承担了恶意用户的可能性!
  • 现在我正在测试输入,它对我来说似乎非常耐用。 JavaScript/SQL/html 一切似乎都得到了照顾。剩下的就是弄清楚如何将所有虚构的标签与真正的&lt;img&gt; 标签交换,同时将图像保留在文本段落之间。
  • @Charles 好点,安全性是我可能错的第一个问题,但跨站点脚本的更好示例可能是[img:http://www.example.com/foo.jpg" onmouseover="alert(document.cookie)],onmouseover 没有结束引号,否则浏览器可能只是厌恶代码而不是有效
【解决方案2】:

如果您不喜欢正则表达式,则不必使用它们。至少不是为了这个目的。

以下应该做:

$in = "[img:http://example.com/_data/025_img.jpg]";

if (strpos($in, "[img:") === 0)
{
    $in = "<img src='" . substr($in, 5, -1) . "' border='0' />";
}

echo $in;

但是,这将是正则表达式:

$in = "[img:http://example.com/_data/025_img.jpg]";

preg_match("~\[img\:(.*?)\]~", $in, $matches);

if ($matches)
{
    echo "<img src='" . $matches[1] . "' border='0' />";
}

简短说明:

模式为:"~\[img\:(.*?)\]~"

我使用~ 作为模式的分隔符。您的起始 [ 必须被转义,因为它是一个正则表达式字符。 img 可以保持原样,: 必须再次转义。之后,任何字符都可以跟随:.* - 问号是将选择变为“不贪婪”,否则将匹配到最后。将其放入(大括号)中,以便将其标记为$matches 的输出。之后,再次关闭] - 仅此而已。

更新:见 Gumbos 评论,: 不需要转义。

【讨论】:

  • : 不必转义。
  • 这只会替换一次出现的模式。用例表明这不是 OP 想要的。
  • @DavidMüller,没有人喜欢 regs,但在这种特殊情况下,使用它们的代码会更加优雅和安全
【解决方案3】:

正则表达式很难,但功能强大。我根本不是大师,所以不认为这是最好的解决方案。

$regEx = '/\[img:http:\/\/[\w]{3,10}\.(com|org|us){1}[\w\/]{5,15}\.(jpg|png|gif){1}\]/i';

$string = 'someting before [img:http://example.com/_data/025_img.png], something after [img:http://example.org/_data/025_img.jpg] and end of the line EOL';
$pstring = $string;
$matches[0] = array();
preg_match_all($regEx, $string, $matches);

匹配数组看起来像:

Array
(
    [0] => Array
        (
            [0] => [img:http://example.com/_data/025_img.png]
            [1] => [img:http://example.org/_data/025_img.jpg]
        )

    [1] => Array
        (
            [0] => com
            [1] => org
        )

    [2] => Array
        (
            [0] => png
            [1] => jpg
        )

)

好的,这里发生了什么:

  1. 正则表达式

/ - 开始正则表达式
\[img:http:\/\/ - 每个字符串都必须以 [img:http:// 开头
[\w]{3,10} - 比我预期的只有 3 到 10 个数字、字母和下划线行,这将是域名(虽然我不确定域名是否必须包含下划线,所以优化点很好)
\. - 点
(com|org|us){1} - 其中一个人
[\w\/]{5,15} - 从五到十五线作为路径,注意我在此处添加了 /
\. - 点
(jpg|png|gif){1} - 其中一个
\] - 模式结束
/i - 制作它不区分大小写

  1. preg_match_all 查找给定字符串中的所有匹配项,从分支到括号中的附加子字符串匹配项作为$matches 的第二个和第三个元素,我猜不到为什么,所以如果有人能帮助理解这一点,将不胜感激。

  2. 接下来使用简单的字符串操作,我可以替换所有主菜

类似这样的:(注意没有if 声明,因为我在开头添加了空的$matches[0],没有ifs 生活会更好:))

foreach ($matches[0] as $match) {
    $img = str_replace(array('[img:',']'), array('<img src="', '" />'), $match);
    $pstring = str_replace($match, $img, $pstring);
}

您可以随意使用正则表达式,根据需要使其变得简单或更复杂。

$pstring 输出是

someting before <img src="http://example.com/_data/025_img.png" />, something after <img src="http://example.org/_data/025_img.jpg" /> and end of the line EOL

这里是游乐场http://phpfiddle.org/main/code/bbu-e24

【讨论】:

  • +1 用于在我开始之前将它们粘合在一起,但你仍然需要在这里担心 XSS。
  • @Charlies,这确实总是需要担心,因为你指出的更详细,现在看复杂的正则表达式,以后不那么头疼
【解决方案4】:
<?php
$str = '[img:http://example.com/_data/025_img.jpg]';
$image = '<img src="'.str_replace(array("[img:","]"),"",$str).'" border="0">';
echo $image;?>

【讨论】:

  • 这会将 [img: ... ] 之前/之后的文本放入 &lt;img&gt; 标记中,这不是解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 2021-11-07
相关资源
最近更新 更多