preg_filter() offers no advantage over preg_replace(). preg_filter() (in fringe cases not represented in the asker's sample data) can mutate the size of the array which would lead to code breakage when implemented with array_multisort() because the sorting atray's size must be the same as the input array.
/[^\n][^\d]+/ actually damages all elements except the first element.
Instead, I recommend preparing the sorting array with preg_replace() and a whole-string pattern with a capture group.
The generated array of numeric strings will be treated as integers by default in the array_multisort().
Code: (Demo)
array_multisort(
preg_replace(
"/\S+\n(\d+).*/s",
'$1',
$tags
),
$tags
);
var_export($tags);
The regex pattern matches non-whitespace characters, then a newline, then captures the full integer value, then greedily matches the rest of the string. The captured numeric string is then used to replace the whole input string. The same behavior is applied to all elements in the array.
Using usort() is less attractive because it would require two function calls per iteration. In other words, just use array_multisort() for best efficiency / fewer function calls.
Below sscanf() is used to parse the predictably formatted string. %*[^\n] will match (but not return) the leading substring upto the first \n, then match and return the second numeric substring as an int-type value.
Code: (Demo)
usort(
$tags,
fn($a, $b) =>
sscanf($a, "%*[^\n]%d")
<=>
sscanf($b, "%*[^\n]%d")
);
var_export($tags);
Or trim all characters prior to the first occurring newline, then cast that string to an integer to remove the unwanted characters. (Demo)
usort(
$tags,
fn($a, $b) =>
(int) strpbrk($a, "\n")
<=>
(int) strpbrk($b, "\n")
);
var_export($tags);
uasort()to define sort rules by yourself.