You can also use this function to repair xml, for example if stray ampersands etc are breaking it:
<?php
$xml = tidy_repair_string($xml, array(
    'output-xml' => true,
    'input-xml' => true
));
?>(PHP 5, PHP 7, PHP 8, PECL tidy >= 0.7.0)
tidy::repairString -- tidy_repair_string — Repara una cadena HTML usando un archivo de configuración opcional
Estilo orientado a objetos
$string, array|string|null $config = null, ?string $encoding = null): string|falseEstilo procedimental
$string, array|string|null $config = null, ?string $encoding = null): string|falseRepara una cadena dada.
stringLos datos a ser reparados.
config
       La configuración config puede ser pasada en forma de un
       array o una cadena. Si una cadena es pasada, será interpretada como el
       el nombre del archivo de configuración, de otra forma, será interpretada como opciones
       en sí mismas.
      
Revise » http://api.html-tidy.org/#quick-reference para una explicación detallada sobre cada opción.
encoding
       El parámetro encoding establece la codificación para
       entarda/salida de los documentos. Los posibles valores de codificación son:
       ascii, latin0, latin1,
       raw, utf8, iso2022,
       mac, win1252, ibm858,
       utf16, utf16le, utf16be,
       big5, y shiftjis.
      
   Devuelve la cadena reparada,  o false si ocurre un error.
  
| Versión | Descripción | 
|---|---|
| 8.0.0 | tidy::repairString() es un método estático ahora. | 
| 8.0.0 | configyencodingson anulables ahora. | 
| 8.0.0 | Esta función ya no acepta el parámetro useIncludePath. | 
Ejemplo #1 Ejemplo de tidy::repairString()
<?php
ob_start();
?>
<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>error</i>
  </body>
</html>
<?php
$buffer = ob_get_clean();
$tidy = new tidy();
$clean = $tidy->repairString($buffer);
echo $clean;
?>El ejemplo anterior mostrará:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN"> <html> <head> <title>test</title> </head> <body> <p>error</p> </body> </html>
You can also use this function to repair xml, for example if stray ampersands etc are breaking it:
<?php
$xml = tidy_repair_string($xml, array(
    'output-xml' => true,
    'input-xml' => true
));
?>The docs referenced at http://tidy.sourceforge.net/docs/quickref.html above state that the configuration option 'sort-attributes' is an enumeration of 'none' and 'alpha', thereby specifying that strings of either form are the acceptable values.  This may not be the case, however - on my system, the option was not honored until I set it to true.  This may also be the case with other options, so experiment a bit.  The output of tidy::getConfig() may be useful in this regard.Using tidy is very simple to fix a broken ods/odt document
I wrote the following code to be run from command line
<?php
$zip = new ZipArchive();
if ($zip->open($argv[1])) {
  $fp = $zip->getStream('content.xml'); //file inside archive
  if(!$fp)
    die("Error: can't get stream to document file");
  $stat = $zip->statName('content.xml');
  $buf = ""; //file buffer
  ob_start(); //to capture CRC error message
    while (!feof($fp)) {
      $buf .= fread($fp, 2048); 
    }
    $s = ob_get_contents();
  ob_end_clean();
  fclose($fp);
  $zip->close();
  $config = array(
      'indent' => true,
      'clean' => true,
      'input-xml'  => true,
      'output-xml' => true,
      'wrap'       => false
  );
  $tidy = new Tidy();
  $xml = $tidy->repairstring($buf, $config);
  $array=split("\n",$xml);
  $file=tempnam("/tmp","xml");
  $fp=fopen($file,"rw+");
  foreach ($array as $key=>$value) {
    fwrite($fp,trim($value),strlen(trim($value)));
    if ($key==0) {
      fwrite($fp,"\n");
    }
  }
  fclose($fp);
  if ($zip->open($argv[1]) === TRUE) {
    $zip->deleteName('content.xml');
    $zip->addFile($file, 'content.xml');
    $zip->close();
    echo 'recovery complete';
  } else {
    echo 'recovery failed';
  }
  unlink($file);
}
?>
save it to a file called fixdoc and invoke as:
php fixdoc yourbrokendoc
for your safety, please work on a copy of your doc.