Ieri ho scritto due righe su un metodo PHP per eliminare l'header e il footer di un file XML.P7M firmato elettronicamente (a patto che sia in formato CAdES). Pur trattandosi di un metodo non particolarmente elegante, possiamo dire che svolge egregiamente il suo lavoro, consentendo di eliminare dal file in questione l'header e il footer contenenti le informazioni relative alla firma.
Oggi verserò un altro tributo alle funzioni "brutte ma (almeno) funzionanti" pubblicando anche la funzione che ho sviluppato a corredo della precedente per ripulire il contenuto del suddetto file da caratteri XML non validi, così da poter utilizzare la stringa risultante come parametro per la creazione di di un oggetto SimpleXML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/** * Removes invalid characters from a UTF-8 XML string * * @access public * @param string a XML string potentially containing invalid characters * @return string */ function sanitizeXML($string) { if (!empty($string)) { // remove EOT+NOREP+EOX|EOT+<char> sequence (FatturaPA) $string = preg_replace('/(\x{0004}(?:\x{201A}|\x{FFFD})(?:\x{0003}|\x{0004}).)/u', '', $string); $regex = '/( [\xC0-\xC1] # Invalid UTF-8 Bytes | [\xF5-\xFF] # Invalid UTF-8 Bytes | \xE0[\x80-\x9F] # Overlong encoding of prior code point | \xF0[\x80-\x8F] # Overlong encoding of prior code point | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2) )/x'; $string = preg_replace($regex, '', $string); $result = ""; $current; $length = strlen($string); for ($i=0; $i < $length; $i++) { $current = ord($string{$i}); if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) { $result .= chr($current); } else { $ret; // use this to strip invalid character(s) // $ret .= " "; // use this to replace them with spaces } } $string = $result; } return $string; } |
Il codice di cui sopra è fortemente basato sul contenuto di due risposte presenti sul sito StackOverflow, per la precisione questa e questa: riconoscimenti e ringraziamenti vanno pertanto ai rispettivi autori. Come è possibile vedere, si tratta di una regexp che elimina tutti i caratteri non UTF-8, seguita da un approccio iterativo char-by-char che elimina tutti i caratteri non considerati validi in un contenuto XML. Nello specifico, ho dovuto utilizzare entrambi gli approcci poiché i file XML che dovevo processare erano affetti da entrambi i problemi.
Come ho detto sopra, si tratta di un metodo piuttosto brutto a vedersi e altamente inefficiente, probabilmente persino più di quello pubblicato in precedenza... Ma nonostante questo si è rivelato perfettamente adatto allo scopo, motivo per cui - vista l'esigua quantità di tempo a disposizione - non mi sono fatto scrupoli a utilizzarlo, almeno come workaround temporaneo in attesa di tempi migliori.
A tal proposito, se a qualcuno venisse l'idea (e la voglia) di fare di meglio, accetterò volentieri il suo suggerimento.... Fino ad allora, direi che il "double-clawed hammer" del PHP ha colpito ancora!
Prometto solennemente di non usarlo più per un pò... :)