0

My goal here is to, within a string, find a supposed array's keys and in that string, replace these keys with the matching keys from the array.

I have a small, useful function that finds all my strings between 2 delimiters (sandbox link: https://repl.it/repls/UnlinedDodgerblueAbstractions):

function findBetween( $string, $start, $end )
{
    $start = preg_quote( $start, '/' );
    $end = preg_quote( $end, '/' );

    $format = '/(%s)(.*?)(%s)/';

    $pattern = sprintf( $format, $start, $end );

    preg_match_all( $pattern, $string, $matches );

    $number_of_matches = is_string( $matches[2] ) ? 1 : count( $matches[2] );

    if( $number_of_matches === 1 ) {
        return $matches[2];
    }

    if( $number_of_matches < 2 || empty( $matches ) ) {
        return False;
    }

    return $matches[2];
}

Example:

findBetween( 'This thing should output _$this_key$_ and also _$this_one$_ so that I can match it with an array!', '_$', '$_')

Should return an array with the values ['this_key', 'this_one'] as it does. Question is, how can I take these and replace them with an associative array's values?

Assume my array is this:

[
    'this_key' => 'love',
    'this_one' => 'more love'
];

My output should be this:

This thing should output love and also more love so that I can match it with an array!

How can I achieve this?

3 Answers 3

1

This is a problem that might be more readily solved with strtr than regex. We can use array_map to add the $start and $end values around the keys of $replacements, and then use strtr to do the substitution:

$str = 'This thing should output _$this_key$_ and also _$this_one$_ so that I can match it with an array!';
$replacements = [
    'this_key' => 'love',
    'this_one' => 'more love'
];
$start = '_$';
$end = '$_';
$replacements = array_combine(array_map(function ($v) use ($start, $end) { return "$start$v$end"; }, array_keys($replacements)), $replacements);
echo strtr($str, $replacements);

Output:

This thing should output love and also more love so that I can match it with an array!

Demo on 3v4l.org

If performance is an issue because you have to regenerate the $replacements array each time, this loop is much faster:

foreach ($replacements as $key => $value) {
    $new_reps["_\$$key\$_"] = $value;
}

Performance comparison demo on 3v4l.org

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

8 Comments

Yo! This is the way to go. Can you explain a bit what's going on here?
@DanielM take a look at the manual for strtr, it has a form in which it can take an array of replacements. What I've done is use array_map to convert the replacements array to look like [ '_$this_key$_' => 'love', '_$this_one$_' => 'more love' ] and then strtr goes over the string making the replacements as described in that array.
Thanks for the explanation, I can see what it's doing, but I've tested and @Julio's answer is way, way faster.
@DanielM that surprises me a lot. I would expect strtr to be much faster than any form of preg_replace, and especially one with the overhead of a function call for every match.
At the same time, this function does parse arrays 3 times and each higher-level parse gets harder the more items there are in the lower ones, all while preg_replace only does a loop internally afaik.
|
1

You may use preg_replace_callback:

<?php

$str = 'This thing should output _$this_key$_ and also _$this_one$_ so that I can match it with an array!';

$replacements = [
    'this_key' => 'love',
    'this_one' => 'more love'
];

$replaced = preg_replace_callback('/_\$([^$]+)\$_/', function($matches) use ($replacements) {
    return $replacements[$matches[1]];
}, $str);

print $replaced;

You have a demo here.

The regular expression, explained:

_              # Literal '_'
\$             # Literal '$' ($ needs to be scaped as it means end of line/string)
(              # Begin of first capturing group
    [^$]+      # One carcter that cannot be "$", repeated 1 or more times
)              # End of first capturing group
\$             # Literal '$'
_              # Literal '_'

For every match, the matching data ($mathces) is passed to the function.

On the first element of the array there is the first capturing group, that we use to do the replacement.

3 Comments

Hey Julio! Can you tell me how does this compare to @Nick's answer?
I actually just went ahead and tested this. Yours is way faster than Nick's. I'm trying to figure out why.
They both work! I just put another alternative using a different approach :) Beware of benchmarking though, unless you are using huge data, benchmarks may be useless. Also, unless performance is critial, I would just use the solution that is easiert to read/mantain.
0

Hope this resolves your answer to the question !

$items['this_key'] = 'love';
$items['this_one'] = 'more love';

$string = 'This thing should output _$this_key$_ and also _$this_one$_ so that I can match it with an array!';

$this_key = $items['this_key'];
$this_one = $items['this_one'];
$string = str_replace('_$this_key$_',$this_key,$string);
$string = str_replace('_$this_one$_',$this_one,$string);

echo  $string;

Comments

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.