3

Consider the following PHP snippet:

<?php

$html = <<<DATA
<p>Lorem Ipsum is simply dummy text</p> <p>Lorem Ipsum is <a href="http://www.google.com">simply</a> dummy text</p><a href="http://www.youtube.com/watch?v=DUQi_R4SgWo" target="_blank" rel="noopener">Check out the video here!</a>. <p>Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</p> <a href="http://www.youtube.com/watch?v=A_6gNZCkajU" target="_blank" rel="noopener">Video here</a> <p>It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
DATA;

# set up the DOM
$dom = new DOMDocument();
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);

# set up the xpath
$xpath = new DOMXPath($dom);

# set up the regex
$regex = '~\?v=([^&]+)~';

foreach ($xpath->query("a[contains(@href, 'youtube')]/@href") as $link) {
    preg_match($regex, $link->nodeValue, $matches);
    if ($matches) {
        $id = $matches[1];
        echo "$id\n";
    }
}
?>

This sets up the DOM on an HTML string and gets the YouTube links with the help of an xpath query and a regular expression afterwards.
The snippet yields

DUQi_R4SgWo
A_6gNZCkajU


Now, I'd like to replace the foreach loop with:

$regex = '~\?v=([^&]+)~';

$xpath->registerPHPFunctions();
$xpath->registerNamespace("php", "http://php.net/xpath");
$links = $xpath->query("a[php:functionString('preg_match', '$regex', href, '$matches')]/@href");

This finds the same links but does not save anything into $matches - why?

7
  • functionString will only accept strings not array and it will convert all variable to string, this is the reason. Commented Nov 20, 2017 at 20:57
  • Yes, they are converted to strings, there is a warning if you run your code. I can only make it work with a custom function. Commented Nov 20, 2017 at 21:03
  • @WiktorStribiżew: I do not get any warning (using PHP 5.6.30) - please add your snippet as an answer though, it comes very near. Commented Nov 20, 2017 at 21:06
  • I'd love to know where the downvote comes from... Commented Nov 20, 2017 at 21:22
  • @Jan this question needs a better, more indicative title, if you don't mind. Commented Feb 8, 2024 at 22:24

2 Answers 2

2

Quick scan of the underlying engine code: it does not support pass-by-reference.

To get around that, use your own wrapper:

$xpath->registerNamespace('php', 'http://php.net/xpath');
$xpath->registerPHPFunctions('match');
$links = $xpath->query("a[php:functionString('match', @href)]/@href");

function match($href) {
    $regex = '~\?v=([^&]+)~';
    $rc = preg_match($regex, $href, $matches);
    var_dump($matches[1]); // store this somewhere
    return $rc;
}

See it live on 3v4l.org.

Sign up to request clarification or add additional context in comments.

6 Comments

As said in the comments, using a custom function solves the problem - thanks a lot, learned something at last.
FYI, there must be /@href at the end: ->query("a[php:functionString('match', @href)]/@href")
Well, it is in the original query.
@WiktorStribiżew: Very true, I needed both.
Fair enough! Updated to include.
|
1
$matches = array();
function mymatch($string){
  $regex = '~\?v=([^&]+)~';
  global $matches;
  preg_match_all($regex, $string, $matches);

}
$xpath->registerPHPFunctions('mymatch');
$xpath->registerNamespace("php", "http://php.net/xpath");
$links = $xpath->query("a[php:functionString('mymatch',@href)]/@href");
print_r($matches);

1 Comment

Code-only answers are low value on StackOverflow. Every time you post an answer please also include an educational explanation as to how it works and why it is advisable. StackOverflow is not merely an archive of solutions, it is a fountain of knowledge that enhances everyone who interacts with it. Please take a moment to improve this answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.