+ /**
+ * Mangle XML-invalid names to be valid in XML
+ * @param string $name
+ * @param array $preserveKeys Names to not mangle
+ * @return string Mangled name
+ */
+ private static function mangleName( $name, $preserveKeys = array() ) {
+ static $nsc = null, $nc = null;
+
+ if ( in_array( $name, $preserveKeys, true ) ) {
+ return $name;
+ }
+
+ if ( $name === '' ) {
+ return '_';
+ }
+
+ if ( $nsc === null ) {
+ // Note we omit ':' from $nsc and $nc because it's reserved for XML
+ // namespacing, and we omit '_' from $nsc (but not $nc) because we
+ // reserve it.
+ $nsc = 'A-Za-z\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{370}-\x{37D}\x{37F}-\x{1FFF}' .
+ '\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}' .
+ '\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}';
+ $nc = $nsc . '_\-.0-9\x{B7}\x{300}-\x{36F}\x{203F}-\x{2040}';
+ }
+
+ if ( preg_match( "/^[$nsc][$nc]*$/uS", $name ) ) {
+ return $name;
+ }
+
+ return '_' . preg_replace_callback(
+ "/[^$nc]/uS",
+ function ( $m ) {
+ return sprintf( '.%X.', UtfNormal\Utils::utf8ToCodepoint( $m[0] ) );
+ },
+ str_replace( '.', '.2E.', $name )
+ );
+ }
+