The problem is that the * is "greedy" by default, so it will match as much as possible. That's making the first id

.*) match all the way to the name: in the last record (because at least one match of that is needed to complete the pattern). You can make the * non-greedy by putting a ? after it, so a pattern like this seems to work:
Code:
$pattern = "/id:(.*?),name:(.*?),ip:(.*?),sa:(.*?),a:(.*?);/si";
Edit: you can also make all matches "ungreedy" by adding the "U" modifier, so this also works:
Code:
$pattern = "/id:(.*),name:(.*),ip:(.*),sa:(.*),a:(.*);/siU";