PHP 替换/匹配 $1 的更好方法


PHP Better way to Replace/Match $1

我正在尝试将URL中的$1, $2, $3变量替换为另一个URL。您可以在下面复制粘贴我的示例并查看我的解决方案。

但我觉得有一种更优雅的方式,带有数组映射类型函数或更好的preg_replace类型。我只需要朝着正确的方向踢一脚,你能帮忙吗?

    <?php
    /**
    *  Key = The DESIRED string
    *  Value = The ORIGINAL value
    *
    * Desired Result: project/EXAMPLE/something/OTHER
    */
    $data = array(
        'project/$1/details/$2' => 'newby/EXAMPLE/something/OTHER'
    );
    foreach($data as $desiredString => $findMe)
    {
        /**
        * Turn these URI's into arrays
        */
        $desiredString = explode('/', $desiredString);
        $findMe = explode('/', $findMe);
        /**
        * Store the array position of the match
        */
        $positions = array();
        foreach($desiredString as $key => $value) {
            /**
            * Look for $1, $2, $3, etc..
            */
            if (preg_match('#('$'d)#', $value)) {
                $positions[$key] = $value;
            }
        }
        /**
        * Loop through the positions
        */
        foreach($positions as $key => $value){
            $desiredString[$key] = $findMe[$key];
        }
        /**
        * The final result
        */
        echo implode('/', $desiredString);
    }

有时你不走运,直接解决问题所需的功能就不存在了。每种语言都会发生这种情况,无论它有多少库和内置。

我们将不得不编写一些代码。我们还需要解决一个特定的问题。最终,我们希望我们对问题的解决方案就像我们最初拥有的理想功能一样干净。因此,无论我们编写什么代码,我们都希望其中大部分不碍事,这可能意味着我们希望大部分代码位于单独的函数或类中。但我们不只是想扔任意代码,因为我们所有的函数和类都应该是可重用的。

然后,我的方法是从解决方案中提取一个有用的通用模式,将其编写为函数,然后使用该函数重写原始解决方案(这将简化它)。为了找到这种一般模式,我把问题变大了,这样它就可以适用于更多情况。

我最终使函数array array_multi_walk(callback $callback [, array $array1 [, array $array2 ... ]]).此函数同时遍历每个数组,并使用$callback来选择要保留的元素。

这就是使用此函数的解决方案的样子。

$chooser = function($a, $b) {
  return strlen($a) >= 2 && $a[0] == '$' && ctype_digit($a[1])
    ? $b : $a;
};
$data = array(
  'project/$1/details/$2' => 'newby/EXAMPLE/something/OTHER'
);
$explodeSlashes = function($a) { return explode('/', $a); };
$find = array_map($explodeSlashes, array_keys($data));
$replace = array_map($explodeSlashes, array_values($data));
$solution = array_multi_walk(
    function($f, $r) use ($chooser) {
        return array_multi_walk($chooser, $f, $r);
    },
    $find, $replace);

而且,根据需要,array_multi_walk可用于其他问题。例如,这会对所有元素求和。

$sum = function() {
  return array_sum(func_get_args());
};
var_dump(array_multi_walk($sum, array(1,2,3), array(1,2,3), array(10)));
// prints the array (12, 4, 6)

您可能需要对array_multi_walk进行一些调整。例如,如果回调按数组而不是单独的参数获取元素可能会更好。也许应该有选项标志在任何数组用完元素时停止,而不是填充空值。

这是我想出的array_multi_walk的实现。

function array_multi_walk($callback)
{
  $arrays = array_slice(func_get_args(), 1);
  $numArrays = count($arrays);
  if (count($arrays) == 0) return array();
  $result = array();
  for ($i = 0; ; ++$i) {
    $elementsAti = array();
    $allNull = true;
    for ($j = 0; $j < $numArrays; ++$j) {
      $element = array_key_exists($i, $arrays[$j]) ? $arrays[$j][$i] : null;
      $elementsAti[] = $element;
      $allNull = $allNull && $element === null;
    }
    if ($allNull) break;
    $result[] = call_user_func_array($callback, $elementsAti);
  }
  return $result;
}

所以在一天结束时,我们不得不编写一些代码,但不仅原始问题的解决方案很流畅,我们还获得了一段通用的、可重用的代码,以便以后帮助我们。

为什么不应该有 $2,$4 而是 $1,$2 ?如果你可以更改数组,那么它可以用 3 或 4 行代码解决。

$data = array(
    'project/$2/details/$4' => 'newby/EXAMPLE/something/OTHER'
);
foreach($data as $desiredString => $findMe)
{
    $regexp = "#(".implode(')/(',explode('/',$findMe)).")#i";
    echo preg_replace($regexp,$desiredString,$findMe);
}

我通过删除注释来缩短您的代码以提高可读性。我正在使用array_map,映射函数决定返回什么值:

<?php
function replaceDollarSigns($desired, $replace)
{
    return preg_match('#('$'d)#', $desired) ? $replace : $desired;
}
$data = array(
    'project/$1/details/$2' => 'newby/EXAMPLE/something/OTHER',
);
foreach($data as $desiredString => $findMe)
{
    $desiredString = explode('/', $desiredString);
    $findMe = explode('/', $findMe);
    var_dump(implode('/', array_map('replaceDollarSigns', $desiredString, $findMe)));
}
?>

工作示例:http://ideone.com/qVLmn

您也可以通过使用create_function省略该函数:

<?php
$data = array(
    'project/$1/details/$2' => 'newby/EXAMPLE/something/OTHER',
);
foreach($data as $desiredString => $findMe)
{
    $desiredString = explode('/', $desiredString);
    $findMe = explode('/', $findMe);
    $result = array_map(
        create_function(
            '$desired, $replace',
            'return preg_match(''#('$'d)#'', $desired) ? $replace : $desired;'
        ),
        $desiredString,
        $findMe);
    var_dump(implode('/', $result));
}
?>

工作示例:http://ideone.com/OC0Ak

只是说,为什么不在preg_replace中使用数组模式/替换?像这样:

<?php
/**
*  Key = The DESIRED string
*  Value = The ORIGINAL value
*
* Desired Result: project/EXAMPLE/something/OTHER
*/
$data = array(
    'project/$1/details/$2' => 'newby/EXAMPLE/details/OTHER'
);
$string = 'project/$1/details/$2';
$pattern[0] = '/'$1/';
$pattern[1] = '/'$2/';
$replacement[0] = 'EXAMPLE';
$replacement[1] = 'OTHER';
$result = preg_replace($pattern, $replacement, $string);
echo $result;

我认为这比您正在寻找的要容易得多。您可以在此处看到它的工作原理:http://codepad.org/rCslRmgs

也许有一些理由保留数组键 => 值来完成替换?