PhP函数用括号替换括号中的单词并将其值存储在数组中[关闭]

PhP函数用括号替换括号中的单词并将其值存储在数组中[关闭]

问题描述:

I am looking for a function for replacing some words in brackets with a specific character and store the values in an array. I am looking for something like this:

example 1: test(word1,word2)-->test(_,_) -->array[word1,word2]

example 2: test(word1(word11),word2,word3(word33(word333,word3333)))-->test(_,_,_) -->array[word1(word11),word2,word3(word33(word333,word3333))]

I think you're going to have to go through the string letter by letter. You tack on each letter into a buffer. Then you keep track of how many left-brackets you've seen after the first one. Every time you come across a comma, you save the previous portion of the string to your collection array, UNLESS you've come across at least one left-parenthesis. In that case, you tack on the comma. Each time you come across a left-bracket, you increment the bracket counter, and each time you come across a right-bracket, you decrement the bracket counter. When the bracket counter get's back down to zero (or in this case, 1), you then go back to the "save previous string" on comma behavior.

Oh, all along while doing this, when you save previous string, you also emit a "_" to the return value / output / post-replaced string, also emitting commas and the like.

At least that's the description of the algorithm, if you can follow it, it should be fairly straightforward.

You can solve your problem using preg_match_all with this kind of pattern: regex101

$pattern = <<<'EOD'
~
(?:
    \G(?!\A)                  # contiguous to a previous match
    (?: ,| \) \z (*ACCEPT) )  # a comma or a closing bracket at the string end
  |                           # OR
    \A (?<func> \w+ ) \(      # the function name at the start of the string
)
(?:
    (?<param>                 # named capture "param"
        (?:
            \w+               # param element
            (?:               # optional part between parenthesis
                \(
                \g<param>     # recursive call to the param subpattern
                (?:, \g<param> )* # optional elements separated by commas 
                \)
            )?
        )
    )
)? # makes the params optional (remove it if you don't want to allow this case)
~x
EOD;

This pattern use two features:

  • the \G anchor that marks the end of the previous match result and is useful to obtain contiguous results. (The advantage of contiguous results is to extract informations you want (as a classical pattern) and at the same time to check the string format).

  • the recursion. The named subpattern 'param' call itself to deal with nested parenthesis.

(*ACCEPT) is a backtracking control verb, when it is reached the pattern succeeds immediatly.

If the string format is correct, you obtain a last match result in which the capture "param" doesn't exist. This is an handy way to test if the string format is correct from the begining to the end of the string.

Lets see it in action with this example code: ideone

class StrFunc {
    private $raw;
    private $isBuild = false;
    private $funcName;
    private $func;
    private $params;
    private $nbParams;
    const PATTERN = '~(?:\G(?!\A)(?:,|\)\z(*ACCEPT))|\A(?<func>\w+)\()(?:(?<param>(?:\w+(?:\(\g<param>(?:,\g<param>)*\))?)))?~';

    public function __construct($raw = null) {
        $this->raw = $raw;  
        if ($raw) $this->createFromRaw();
    }

    public function createFromRaw() {
        if ( !$this->isBuild &&
             preg_match_all(self::PATTERN, $this->raw, $m, PREG_SET_ORDER) &&
             !isset(end($m)['param']) ) {
            $this->params = array_column($m, 'param');
            $this->funcName = $m[0]['func'];
            $this->nbParams = count($this->params);
            $this->func = $this->funcName . '(' 
                        . ( ($this->nbParams)
                          ? implode(',', array_fill(1, $this->nbParams, '_'))
                          : '' )
                        . ')';
            $this->isBuild = true;
        }
    }

    public function setRaw($raw) {
        $this->isBuild = false;
        $this->raw = $raw;
    }

    public function getFuncName() {
        return ($this->isBuild) ? $this->funcName : $this->isBuild; 
    }

    public function getFunc() {
        return ($this->isBuild) ? $this->func : $this->isBuild;
    }

    public function getParams() {
        return ($this->isBuild) ? $this->params : $this->isBuild;
    }
};

$tests = array('test(word1,word2)',
               'huitre(word1(word11),word2,word3(word33(word333,word3333)))',
               'splitz()');

foreach ($tests as $k=>$test) {
    $strFunc[$k] = new StrFunc($test);
    echo $strFunc[$k]->getFuncName() . "
"
       . $strFunc[$k]->getFunc() . "
"
       . print_r($strFunc[$k]->getParams(), true) . "
";
}

This example code is written for PHP 5.5. To adapt it to a previous PHP version you need to replace $this->params = array_column($m, 'param'); with:

foreach($m as $match) {
    if (isset($match['param']))
        $this->params[] = $match['param'];
}

If you experience problems with !isset(end($m)['param']) you can replace it with:

!isset($m[count($m)-1]['param'])