Here is my approach using DomDocument, to isolate attribute declarations, then splitting the style declarations into a subarray. I've expanded the input string to express how fringe cases will be stored in the result array. Demo
$attribs = ' id= "header " class = "foo bar" style ="background-color:#fff; color: red; " disabled readonly checked hidden required data-test="just checking"';
$result = [];
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML("<p $attribs>");
foreach ($dom->getElementsByTagName('p')->item(0)->attributes as $attr) {
$name = $attr->name;
if (isset($result[$name])) {
continue; // as HTML, JS, and CSS will behave, ignore subsequent duplicate attributes
}
$value = trim($attr->value);
if ($name === 'style') {
$value = array_reduce(
explode(';', $attr->value),
function ($res, $pair) {
if (sscanf($pair, '%[^:]:%[^;]', $k, $v) === 2) {
$res[trim($k)] = trim($v);
}
return $res;
}
);
}
$result[$name] = $value;
}
var_export($result);
Output:
array (
'id' => 'header',
'class' => 'foo bar',
'style' =>
array (
'background-color' => '#fff',
'color' => 'red',
),
'disabled' => 'disabled',
'readonly' => 'readonly',
'checked' => 'checked',
'hidden' => '',
'required' => '',
'data-test' => 'just checking',
)