Using regular expressions to manipulate valid HTML is not advisable because regex is "DOM-unaware" meaning that it doesn't know if it is actually modifying a tag or just text that _seems_ like a tag.  Also, unless expressly told to replace only once, it will happily modify multiple matching strings (effectively damaging your content).  Finally, using DomDocument is a superior technique because it will intelligently/reliably parse and modify a class attribute regardless of the position of the attribute in the tag and regardless of the character used to quote the value (single or double quotes).

Code: ([PHP Demo][1])

    $new_class = "new_class_attr";
    
    $body = JFactory::getApplication()->getBody();
    
    $dom = new DOMDocument; 
    $dom->loadHTML($body, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $bodytag = $dom->getElementsByTagName('body')->item(0);
    if ($bodytag->hasAttribute("class")) {
        $new_class = $bodytag->getAttribute("class") . " " . $new_class;
    }
    $bodytag->setAttribute("class", $new_class);
    JFactory::getApplication()->setBody($dom->saveHTML());

---

And just in case anyone mistakes my endorsement of DOMDocument for fear of regular expressions, here is how I would code it up using refined regex calls.

Code: ([PHP Demo][2]) ([First Regex Demo][3]) ([Second Regex Demo][4])

    $new_class = "new_class_attr";
    
    $body = JFactory::getApplication()->getBody();
    
    $body = preg_replace('~<body[^>]*class *= *["\']\K~', "$new_class ", $body, 1, $count);  // add to existing class attribute
    if (!$count) {
        $body = preg_replace('~<body\K~', " class=\"$new_class\"", $body, 1);  // create class with attribute
    }
    JFactory::getApplication()->setBody($body);

But, again, I don't recommend regex for this job because it will be less stable... Imagine if you had an attribute (and didn't realize it) like `specialclass` or `data-class`.  So look, let's not go down the tit-for-tat rabbit hole of creating a body string that will break the pattern then adjust the pattern to suit the vulnerability -- just use a DOM parser.

  [1]: https://3v4l.org/o6XkP
  [2]: https://3v4l.org/MVHoY
  [3]: https://regex101.com/r/oMFUhr/1
  [4]: https://regex101.com/r/oMFUhr/2