我使用Parsedown将HTML从数据库解析到我的网站。使用Parsedown,您无法在链接中添加target="_blank"
。
所以我要做的就是将target="_blank"
添加到外部链接。我在Parsedown.php中找到了这个功能:
protected function inlineLink($Excerpt)
{
$Element = array(
'name' => 'a',
'handler' => 'line',
'text' => null,
'attributes' => array(
'href' => null,
'title' => null,
),
);
$extent = 0;
$remainder = $Excerpt['text'];
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
{
$Element['text'] = $matches[1];
$extent += strlen($matches[0]);
$remainder = substr($remainder, $extent);
}
else
{
return;
}
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
if (isset($matches[2]))
{
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
}
$extent += strlen($matches[0]);
}
else
{
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
$definition = strtolower($definition);
$extent += strlen($matches[0]);
}
else
{
$definition = strtolower($Element['text']);
}
if ( ! isset($this->DefinitionData['Reference'][$definition]))
{
return;
}
$Definition = $this->DefinitionData['Reference'][$definition];
$Element['attributes']['href'] = $Definition['url'];
$Element['attributes']['title'] = $Definition['title'];
}
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
return array(
'extent' => $extent,
'element' => $Element,
);
}
现在,我尝试过的是(添加了对我所做更改的评论):
protected function inlineLink($Excerpt)
{
$Element = array(
'name' => 'a',
'handler' => 'line',
'text' => null,
'attributes' => array(
'href' => null,
'target' => null, // added this
'title' => null,
),
);
$extent = 0;
$remainder = $Excerpt['text'];
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
{
$Element['text'] = $matches[1];
$extent += strlen($matches[0]);
$remainder = substr($remainder, $extent);
}
else
{
return;
}
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
if (isset($matches[2]))
{
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
}
$extent += strlen($matches[0]);
}
else
{
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
$definition = strtolower($definition);
$extent += strlen($matches[0]);
}
else
{
$definition = strtolower($Element['text']);
}
if ( ! isset($this->DefinitionData['Reference'][$definition]))
{
return;
}
$Definition = $this->DefinitionData['Reference'][$definition];
$Element['attributes']['href'] = $Definition['url'];
if (strpos($Definition['url'], 'example.com') !== false) { // added this aswell, checking if its our own URL
$Element['attributes']['target'] = '_blank';
}
$Element['attributes']['title'] = $Definition['title'];
}
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
return array(
'extent' => $extent,
'element' => $Element,
);
}
有任何建议可以实现这一目标吗?
答案 0 :(得分:1)
GitHub上的问题already exists。请参阅this评论。
我的扩展程序可以自动设置rel =“nofollow”和target =“_ blank” 检测到外部链接时链接的属性。您可以 还可以通过属性块手动设置这些属性:
[text](http://example.com) {rel="nofollow" target="_blank"}
Automatic rel="nofollow" Attribute on External Links
// custom external link attributes $parser->links_external_attr = array( 'rel' => 'nofollow', 'target' => '_blank' );
如果您想在不使用parsedown-extra-plugin扩展名的情况下在Parsedown类中进行更改,您可以执行以下操作:
1)在the first line \Parsedown::element
之后的$markup = '<'.$Element['name'];
方法中添加此行$Element = $this->additionalProcessElement($Element);
2)向Parsedown类添加新方法:
protected function additionalProcessElement($Element) { }
3)扩展Parsedown类并将其保存为MyParsedown.php文件:
<?php
namespace myapps;
require_once __DIR__.'/Parsedown.php';
/**
* Class MyParsedown
* @package app
*/
class MyParsedown extends \Parsedown
{
/**
* @param array $Element
* @return array
*/
protected function additionalProcessElement($Element)
{
if ($Element['name'] == 'a' && $this->isExternalUrl($Element['attributes']['href'])) {
$Element['attributes']['target'] = '_blank';
}
return $Element;
}
/**
* Modification of the funciton from answer to the question "How To Check Whether A URL Is External URL or Internal URL With PHP?"
* @param string $url
* @param null $internalHostName
* @see https://stackoverflow.com/a/22964930/7663972
* @return bool
*/
protected function isExternalUrl($url, $internalHostName = null) {
$components = parse_url($url);
$internalHostName = ($internalHostName == null) ? $_SERVER['HTTP_HOST'] : $internalHostName;
// we will treat url like '/relative.php' as relative
if (empty($components['host'])) {
return false;
}
// url host looks exactly like the local host
if (strcasecmp($components['host'], $internalHostName) === 0) {
return false;
}
$isNotSubdomain = strrpos(strtolower($components['host']), '.'.$internalHostName) !== strlen($components['host']) - strlen('.'.$internalHostName);
return $isNotSubdomain;
}
}
4)创建test.php
文件并运行它:
require_once __DIR__.'/MyParsedown.php';
$parsedown = new \myapps\MyParsedown();
$text = 'External link to [example.com](http://example.com/abc)';
echo $parsedown->text($text);
此HTML代码将显示在浏览器页面上(如果您的主机当然不是example.com):
<p>External link to <a href="http://example.com/abc" target="_blank">example.com</a></p>
答案 1 :(得分:1)
今天进入这个问题。我想让来自不同主机的所有链接自动在新目标中打开。不幸的是,接受的答案建议编辑Parsedown类文件,这是一个坏主意imo。
我创建了一个扩展Parsedown
的新PHP类,并为element
方法创建了一个覆盖。这是全班:
class ParsedownExtended extends Parsedown
{
protected function element(array $Element)
{
if ($this->safeMode) {
$Element = $this->sanitiseElement($Element);
}
$markup = '<' . $Element['name'];
if (isset($Element['name']) && $Element['name'] == 'a') {
$server_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
$href_host = isset($Element['attributes']['href']) ? parse_url($Element['attributes']['href'], PHP_URL_HOST) : null;
if ($server_host != $href_host) {
$Element['attributes']['target'] = '_blank';
}
}
if (isset($Element['attributes'])) {
foreach ($Element['attributes'] as $name => $value) {
if ($value === null) {
continue;
}
$markup .= ' ' . $name . '="' . self::escape($value) . '"';
}
}
if (isset($Element['text'])) {
$markup .= '>';
if (!isset($Element['nonNestables'])) {
$Element['nonNestables'] = array();
}
if (isset($Element['handler'])) {
$markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
}
else {
$markup .= self::escape($Element['text'], true);
}
$markup .= '</' . $Element['name'] . '>';
}
else {
$markup .= ' />';
}
return $markup;
}
}
这就是魔术发生的地方:
if (isset($Element['name']) && $Element['name'] == 'a') {
$server_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
$href_host = isset($Element['attributes']['href']) ? parse_url($Element['attributes']['href'], PHP_URL_HOST) : null;
if ($server_host != $href_host) {
$Element['attributes']['target'] = '_blank';
}
}
现在我在解析内容时只使用ParsedownExtended
代替Parsedown
,例如:
$parsedown = new ParsedownExtended();
return $parsedown->text($this->body);
希望这有助于某人。
答案 2 :(得分:0)
就像kjdion84一样,我还将扩展Parsedown
类。我建议不要复制和更改element
方法,而要覆盖inlineLink
;如果更改了基础代码,则工作量更少,并且将来有更多证据。
注意:urlIsExternal
方法绝不完整(缺少主机检查)。
class ParsedownExtended extends Parsedown
{
protected function inlineLink($Excerpt)
{
$link = parent::inlineLink($Excerpt);
if ($this->urlIsExternal($link['element']['attributes']['href'])) {
$link['element']['attributes'] += [
'target' => '_blank',
'rel' => 'nofollow',
];
}
return $link;
}
protected function urlIsExternal($url)
{
$scheme = parse_url($url, PHP_URL_SCHEME);
$host = parse_url($url, PHP_URL_HOST);
if (!$scheme || !$host) {
return false;
}
if (strpos(strtolower($scheme), 'http') !== 0) {
return false;
}
// @TODO check the host
return true;
}
}