tags:

views:

249

answers:

6

I need to convert

$text = 'We had <i>fun</i>. Look at <a href="http://example.com"&gt;this photo</a> of Joe';

[Edit] There could be multiple links in the text.

to

$text = 'We had fun. Look at this photo (http://example.com) of Joe';

All HTML tags are to be removed and the href value from <a> tags needs to be added like above.

What would be an efficient way to solve this with regex? Any code snippet would be great.

+1  A: 

try an xml parser to replace any tag with it's inner html and the a tags with its href attribute.

http://www.php.net/manual/en/book.domxml.php

Javier Parra
+5  A: 

You said no regex, but they do make your life easy here. Just match the URL.

preg_match_all('/href="([^"]+)"/', $text, $m);
$text = str_replace('</a>', ' (' . $m[1][0] . ')', $text);
$text = strip_tags($text);
Timothy
In this case regex FTW!
Yeti
@Lost_in_code this doesn't work if you got more than one link in $text or if there is a link element with an href attribute or if someone just wrote href="something" somewhere as regular text.
Gordon
@Gordon indeed! Realized that when I woke up this morning :)
Yeti
+5  A: 

First do a preg_replace to keep the link. You could use:

preg_replace('<a href="(.*?)">(.*?)</a>', '$\2 ($\1)', $str);

Then use strip_tags which will finish off the rest of the tags.

nc3b
This won't work well, as it's been thoroughly explained in here html is too complex to be parsed using regex.For instance, that simple one will break when using single quotes instead of doubles in the href attribute (to fix that change the first double quote with: ([\'\"]) and the second with a backreference)
Javier Parra
I agree. (X)HTML is _complex_ and one should think twice before parsing it with a regular expression. That said, for a quick one-off DOM might be overkill.
nc3b
@Lost_in_code This will fail if the user added any other attribute to the link, e.g `<a class="foo" href="...` or title or rel or whatever else is possible there. It will also not work with `<a href = "..."` or uppercase, etc. - just believe us: Regex sucks for this :)
Gordon
A: 

I've done things like this using variations of substring and replace. I'd probably use regex today but you wanted an alternative so:

For the <i> tags, I'd do something like:

$text = replace($text, "<i>", "");
$text = replace($text, "</i>", "");

(My php is really rusty, so replace may not be the right function name -- but the idea is what I'm sharing.)

The <a> tag is a bit more tricky. But, it can be done. You need to find the point that <a starts and that the > ends with. Then you extract the entire length and replace the closing </a>

That might go something like:

$start = strrpos( $text, "<a" );
$end = strrpos( $text, "</a>", $start );
$text = substr( $text,  $start, $end );
$text = replace($text, "</a>", "");

(I don't know if this will work, again the idea is what I want to communicate. I hope the code fragments help but they probably don't work "out of the box". There are also a lot of possible bugs in the code snippets depending on your exact implementation and environment)

Reference:

Frank V
Thanks for this, but I've edited the question, since regex seems to be the way to go. It's much simpler and *quick*.
Yeti
+1  A: 

The DOM solution:

$dom = new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
foreach($xpath->query('//a[@href]') as $node) {
    $textNode = new DOMText(sprintf('%s (%s)',
        $node->nodeValue, $node->getAttribute('href')));
    $node->parentNode->replaceChild($textNode, $node);
}
echo strip_tags($dom->saveHTML());

and the same without XPath:

$dom = new DOMDocument;
$dom->loadHTML($html);
foreach($dom->getElementsByTagName('a') as $node) {
    if($node->hasAttribute('href')) {
        $textNode = new DOMText(sprintf('%s (%s)',
            $node->nodeValue, $node->getAttribute('href')));
        $node->parentNode->replaceChild($textNode, $node);
    }
}
echo strip_tags($dom->saveHTML());

All it does is load any HTML into a DomDocument instance. In the first case it uses an XPath expression, which is kinda like SQL for XML, and gets all links with an href attribute. It then creates a text node element from the innerHTML and the href attribute and replaces the link. The second version just uses the DOM API and no Xpath.

Yes, it's a few lines more than Regex but this is clean and easy to understand and it won't give you any headaches when you need to add additional logic.

Gordon
A: 

It's also very easy to do with a parser:

# available from http://simplehtmldom.sourceforge.net
include('simple_html_dom.php');

# parse and echo
$html = str_get_html('We had <i>fun</i>. Look at <a href="http://example.com"&gt;this photo</a> of Joe');

$a = $html->find('a');
$a[0]->outertext = "{$a[0]->innertext} ( {$a[0]->href} )";

echo strip_tags($html);

And that produces the code you want in your test case.

Erik