16

I'm trying to replace {{key}} items in my $text with values from a passed array. but when I tried adding the print_r to see what was going on I got a Undefined variable: kvPairs error. How can I access my variable form within the preg_replace_callback?

public function replaceValues($kvPairs, $text) {
    $text = preg_replace_callback(
        '/(\{{)(.*?)(\}})/',
        function ($match) {
            $attr = trim($match[2]);
            print_r($kvPairs[strtolower($attr)]);
            if (isset($kvPairs[strtolower($attr)])) {
                return "<span class='attr'>" . $kvPairs[strtolower($attr)] . "</span>";
            } else {
                return "<span class='attrUnknown'>" . $attr . "</span>";
            }
        },
        $text
    );
    return $text;
}

Update:

I've tried the global scope thing, but it doesn't work either. I've added 2 print statements to see whats doing on, one inside and one outside the preg_replace_callback.

public function replaceValues($kvPairs, $text) {
    $attrTest = 'date';
    print_r("--" . strtolower($attrTest) . "--" . $kvPairs[strtolower($attrTest)] . "--\n");
    $text = preg_replace_callback(
        '/(\{{)(.*?)(\}})/',
        function ($match) {
            global $kvPairs;
            $attr = trim($match[2]);
            print_r("==" . strtolower($attr) . "==" . $kvPairs[strtolower($attr)] . "==\n");
            if (isset($kvPairs[strtolower($attr)])) {
                return "<span class='attr'>" . $kvPairs[strtolower($attr)] . "</span>";
            } else {
                return "<span class='attrUnknown'>" . $attr . "</span>";
            }
        },
        $text
    );
    return $text;
}

The output I get is:

--date--1977-05-20--
==date====
Justin808
  • 20,859
  • 46
  • 160
  • 265

2 Answers2

50

As your callback function is a closure, you can pass extra arguments via use

function ($match) use ($kvPairs) {
    ...
}

better than polluting the global space

Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • I would like to manipulate $kvPairs for the next match, ist this possible or do I need a global variable for it? – Wikunia Aug 26 '14 at 11:33
  • 8
    If you need to manipulate `$kvPairs` within the callback then you pass it ___by reference___: `function ($match) use (&$kvPairs) { ... }` – Mark Baker Aug 26 '14 at 11:36
0

Your script can be modernized and refined to allow access to globally scoped values without use(). Arrow function syntax from PHP7.4 allows access to variables which were declared outside of the callback's scope.

I've removed some unnecessary escaping and capture groups from your pattern. Adding some space-consuming logic on either side of the capture group affords the omission of trim() calls on the captured string.

Code: (Demo)

function replaceValues(array $kvPairs, string $text): string
{
    return preg_replace_callback(
               '/{{\s*(.*?)\s*}}/',
                fn($m) => isset($kvPairs[strtolower($m[1])])
                    ? '<span class="attr">' . $kvPairs[strtolower($m[1])] . '</span>'
                    : '<span class="attrUnknown">' . $m[1] . '</span>',
                $text
            );
}

Otherwise, this is a duplicate of Callback function using variables calculated outside of it.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136