Home > Software engineering >  I wrote a php function to add <span></span> to every numeric occurrence in a piece of te
I wrote a php function to add <span></span> to every numeric occurrence in a piece of te

Time:08-20

I've made couple of version but all of them do not work, here's one of them.

function addSpanv3($content) {
    preg_match_all('!\d \.*\d*!', $content, $matches);
    $matches[0] = array_unique($matches[0]);
    rsort($matches[0]); // number must be sorted in an ascending fashion
    $sorted_matches = $matches[0];
    $splitedStrings = array();
    $temp_content = $content;
    for ($i = 0; $i < count($sorted_matches); $i  ) {
        $match = $sorted_matches[$i];
        if (is_numeric($match)) {
            $match_regex = "/(".$match.")/"; // tested correct, matches won't repeat
            $replace_with = '<span style="padding = 0; font-family: Arial">'.$match.'</span>';
            $temp_content = str_replace($match, $replace_with, $temp_content);
        }
    }

Sample text: Various names for the island of Taiwan remain in use, each derived from explorers or rulers during a particular historical period. The name Formosa dates from 1,542, when Portuguese sailors sighted an uncharted island and noted it on their maps as Ilha Formosa ("beautiful island").[48.49] The name Formosa eventually "replaced all others in European literature"[50] and remained in common use among English speakers into the 2000 century.[51]

CodePudding user response:

You can do it using regex word boundaries-

\b assert position at a word boundary
[0-9] matches numbers
  matches the previous token between one and unlimited times, as many times as possible, giving back as needed
\b at the end to assert end of word boundry
<?php

print_r(span('Various names for the island of Taiwan remain in use, each derived from explorers or rulers during a particular historical period. The name Formosa dates from 1,542, when Portuguese sailors sighted an uncharted island and noted it on their maps as Ilha Formosa ("beautiful island").[48.49] The name Formosa eventually "replaced all others in European literature"[50] and remained in common use among English speakers into the 2000 century.[51]'));

function span($str) {
    $pattern = '/\b[0-9] \b/m';
    $replacement = '<span>$0</span>';
    return preg_replace($pattern , $replacement , $str);
}

CodePudding user response:

I think you are over-complicating this. Have you looked into

https://www.php.net/manual/en/function.preg-replace.php?

The solution

Here is a proof of concept for what you are trying to do:

<?php
$test = "Various names for the island of Taiwan remain in use, each derived from explorers or rulers during a particular historical period. The name Formosa dates from 1,542, when Portuguese sailors sighted an uncharted island and noted it on their maps as Ilha Formosa ('beautiful island').[48.49] The name Formosa eventually 'replaced all others in European literature'[50] and remained in common use among English speakers into the 2000 century.[51]";
$pattern = "/(\d{1,3}(,\d{3})*(\.\d )?)/i";
$replacement = "<span style='padding = 0; font-family: Arial'>$1</span>";
$result = preg_replace($pattern, $replacement, $test);
echo $result;

Let me explain what is going on here. First of all, you needed a better regex that would actually match the numbers you are looking for, but match them in a matching group. I can't claim credit for the regex, I found it with a google search. Here is the source of the regex:

Regex for number with decimals and thousand separator

Now, in the replacement string, we can reference the matching group with $# where # is the number of the group matched. In our case, we only have 1 group per match, but you could have multiple depending on the regex. for instance, /(\d), (\d)/ would have two matching groups for the string 100, 200. $1 would contain 100, and $2 would contain 200 in this example.

So in this case, we find all instances using our regex, and for each matched group, we wrap it in your span tags!


The Advice

Now, I would be remiss if I did not say that what you are doing has a lot of code smells. I don't know what your situation is where you have to do this (I have seen a lot of things in my career), but just in case you are starting out in your career (or someone else comes along and sees this), let me give you a few tips:

  1. Adding CSS inline like that is an anti-pattern; use CSS classes or ids
  2. Do your best to keep your logic and your display separate. I imagine you need this for some sort of server-side rendering helper. I imagine a lot of the downvotes are people who are triggered by seeing HTML in a string like this. What you are doing is coupling display details to your method. It would be marginally better to take the thing you want to wrap the numbers with as a parameter. What you have here is a tight coupling that will be difficult to maintain down the road.
  3. addSpanv3 is a smell in and of itself. Use version control; this is exactly what it is for. Experiment, try things. if they don't work, you can go back in your commit history and find your earlier versions. Don't litter your codebase with addSpan1, addSpan2, etc. that gets out of hand extremely fast.
  4. addSpan is a uni-tasker. think of this more like wrapNumbers, and let the thing you wrap it with be configurable. decouple your responsibilities!

Anyways, I just wanted to leave this advice. for all I know you have a nightmare codebase where this is the only way to accomplish what you need, and I totally relate to that.

  • Related