The only single function call approach to directly generate the desired output involves a capture group inside of a lookahead called by preg_split()
(a simpler pattern could be made for preg_match_all()`, but it generates a 2d array instead of a 1d array.
Code: (Demo)
var_export(
preg_split(
"/(?=(\S+ \S+))\S+ (?:\S+$)?/",
$string,
0,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
)
);
preg_match()
version: (Demo)
preg_match_all(
"/(?=(\S+ \S+))\S+ /",
$string,
$m
);
var_export($m[1]);
Or because you only have words and spaces: Demo
preg_match_all(
"/(?=(\b\w+ \w+))/",
$string,
$m
);
var_export($m[1]);
Using the same splitting regex as demonstrated in Split string on every second space to isolate every two words will work if you pre-inject the duplicated words which are lead and trailed by a space.
Code: (Demo)
var_export(
preg_split(
"/\S+ \S+\K /",
preg_replace(
'/(?<= )(\S+ )\K/',
'$1',
$string
)
)
);
Or use explode()
with a static
variable to hold the previous word for the next iteration inside of array_reduce()
. (Demo)
var_export(
array_reduce(
explode(' ', $string),
function($result, $word) {
static $last = '';
if ($last) {
$result[] = "$last $word";
}
$last = $word;
return $result;
}
)
);
Or a classic loop over the exploded string while holding the previous iteration's word. (Demo)
$result = [];
$last = null;
foreach (explode(' ', $string) as $word) {
if ($last) {
$result[] = "$last $word";
}
$last = $word;
}
var_export($result);
Or explode then append and unset data: (Demo)
$result = explode(' ', $string);
foreach ($result as $i => $word) {
if (isset($result[$i + 1])) {
$result[$i] .= " {$result[$i + 1]}";
} else {
unset($result[$i]);
}
}
var_export($result);
Basic for()
loop with n-1 iterations (won't display a lone word string): (Demo)
$words = explode(' ', $string);
$result = [];
for ($i = 1, $max = count($words); $i < $max; ++$i) {
$result[] = $words[$i - 1] . ' ' . $words[$i];
}
var_export($result);