我认为仅限于使用 str_replace 时的主要问题是您几乎无法控制哪些字符串已被替换(因为所有匹配项都被一次替换)并且在选择占位符时需要特别小心\@ 转义序列。有可能两个插入的值结合起来会产生占位符字符串,因此当占位符替换被还原时会变成一个@ 字符。
以下是尝试处理该问题的蛮力解决方案。它一次检查一个占位符与模板字符串、替换值和最终字符串,确保占位符不会出现在任何这些字符串中,并且最初为 \@ 引入的占位符数量与占位符的数量相匹配恢复。您可能希望设置一个最适合您的默认占位符而不是 xyz(如零字符或其他内容),以避免不必要的处理。
可以使用两种替换模式(@ 和@<n>)调用它,但目前它们不能混合使用。
这不是我写过的最漂亮的代码,但考虑到str_replace 的约束,它仍然是我的尝试,我希望它可以对你有所帮助。
function templateMap ($string, $array, $defaultPlaceholder = "xyz")
{
$newArray = array();
// Create an array of the subject string and replacement arrays
$knownStrings = array($string);
foreach ($array as $subArray) {
if (is_array($subArray)) {
$knownStrings = array_merge($knownStrings, array_values($subArray));
}
else {
$knownStrings[] = $subArray;
}
}
$placeHolder = '';
while (true) {
if (!$placeHolder) {
// This is the first try, so let's try the default placeholder
$placeHolder = $defaultPlaceholder;
}
else {
// We've been here before - we need to try another placeholder
$placeHolder = uniqid('bs-placeholder-', true);
}
// Try to find a placeholder that does not appear in any of the strings
foreach ($knownStrings as $knownString) {
// Does $placeHolder exist in $knownString?
str_replace($placeHolder, 'whatever', $knownString, $count);
if ($count > 0) {
// Placeholder candidate was found in one of the strings
continue 2; // Start over
}
}
// Will go for placeholder "$placeHolder"
foreach ($array as $subArray) {
$newString = $string;
// Apply placeholder for \@ - remember number of replacements
$newString = str_replace(
'\@', $placeHolder, $newString, $numberOfFirstReplacements
);
if (is_array($subArray)) {
// Make substitution on @<n>
for ($i = 0; $i <= 9; $i++) {
@$newString = str_replace("@$i", $subArray[$i], $newString);
}
}
else {
// Make substitution on @
@$newString = str_replace("@", $subArray, $newString);
}
// Revert placeholder for \@ - remember number of replacements
$newString = str_replace(
$placeHolder, '@', $newString, $numberOfSecondReplacements
);
if ($numberOfFirstReplacements != $numberOfSecondReplacements) {
// Darn - value substitution caused used placeholder to appear,
// ruining our day - we need some other placeholder
$newArray = array();
continue 2;
}
// Looks promising
$newArray[] = $newString;
}
// All is well that ends well
break;
}
return $newArray;
}
$a = templateMap(
"(@ and one escaped \@)",
array(" col1 < 5 && col2 > 6", " col3 < 3 || col4 > 7")
);
print_r($a);
$a = templateMap(
"You can tweet @0 \@2 @1",
array(
array("Sarah", "ssarahtweetz"),
array("John", "jjohnsthetweetiest"),
)
);
print_r($a);
输出:
Array
(
[0] => ( col1 < 5 && col2 > 6 and one escaped @)
[1] => ( col3 < 3 || col4 > 7 and one escaped @)
)
Array
(
[0] => You can tweet Sarah @2 ssarahtweetz
[1] => You can tweet John @2 jjohnsthetweetiest
)