Comment vérifier si string est un nom D'élément XML valide?

j'ai besoin d'un regex ou d'une fonction en PHP qui va valider une chaîne de caractères pour être un bon nom D'élément XML.

Formulaire de w3schools:

les éléments XML doivent suivre ces désignations les règles:

  1. Noms peuvent contenir des lettres, des chiffres et d'autres caractères
  2. les noms ne peuvent pas commencer par un nombre ou un caractère de ponctuation
  3. les noms ne peuvent pas commencer par les lettres xml (ou XML, ou Xml, etc)
  4. Noms ne peuvent pas contenir des espaces

je peux écrire un regex de base qui vérifiera les règles 1,2 et 4, mais il ne tiendra pas compte de toute la ponctuation permise et ne tiendra pas compte de la 3e règle

w[w0-9-]

Amicale De Mise À Jour

<!-Voici la source qui fait le plus autorité pour noms D'éléments XML bien formés:

les Noms et les Jetons

NameStartChar   ::=
    ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] |
    [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | 
    [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | 
    [#x10000-#xEFFFF]

NameChar    ::=
    NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]

Name    ::=
    NameStartChar (NameChar)*

une règle séparée non tokenized est aussi spécifiée:

Noms en commençant par la chaîne "xml", ou par toute chaîne qui correspondrait ('X'|'x') ('M'|'m') ('L'|'l')), sont réservées à la standardisation dans cette version ou les versions futures de cette spécification.

18
demandé sur Anthony 2010-03-26 01:20:57

10 réponses

Que Diriez-vous de

/\A(?!XML)[a-z][\w0-9-]*/i

Utilisation:

if (preg_match('/\A(?!XML)[a-z][\w0-9-]*/i', $subject)) {
    # valid name
} else {
    # invalid name
}

Explication:

\A  Beginning of the string
(?!XML)  Negative lookahead (assert that it is impossible to match "XML")
[a-z]  Match a non-digit, non-punctuation character
[\w0-9-]*  Match an arbitrary number of allowed characters
/i  make the whole thing case-insensitive
8
répondu Leo 2010-03-26 14:16:21

si vous voulez créer valid XML, DOM Extension. De cette façon, vous n'avez pas à vous soucier de tout Regex. Si vous essayez de mettre dans un nom invalide à un DomElement, vous obtiendrez une erreur.

function isValidXmlName($name)
{
    try {
        new DOMElement($name);
        return TRUE;
    } catch(DOMException $e) {
        return FALSE;
    }
}

donner

var_dump( isValidXmlName('foo') );      // true   valid localName
var_dump( isValidXmlName(':foo') );     // true   valid localName
var_dump( isValidXmlName(':b:c') );     // true   valid localName
var_dump( isValidXmlName('b:c') );      // false  assumes QName

et est probablement assez bon pour ce que vous voulez faire.

Pédant note 1

notez la distinction entre localName et Nom. ext / dom suppose que vous utilisez un élément namespaced s'il y a un préfixe avant le deux-points, ce qui ajoute des contraintes à la façon dont le nom peut être formé. Techniquement, b:b est un nom local valide cependant parce que NameStartChar fait partie de NameChar. Si vous voulez les inclure, changez la fonction en

function isValidXmlName($name)
{
    try {
        new DOMElement(
            $name,
            null,
            strpos($name, ':') >= 1 ? 'http://example.com' : null
        );
        return TRUE;
    } catch(DOMException $e) {
        return FALSE;
    }
}

Pédant note 2

Notez que les éléments peuvent commencer par "xml". W3Schools (qui n'est pas affilié au W3c) s'est apparemment trompé sur cette partie (ne serait pas la première temps). Si vous voulez vraiment exclure des éléments commençant par xml ajouter

if(stripos($name, 'xml') === 0) return false;

avant try/catch.

20
répondu Gordon 2013-03-04 10:39:37

cela a été manqué jusqu'à présent malgré le fait que la question Est que l'ancienne: la validation du nom via les fonctions pcre de PHP qui sont rationalisées avec la spécification XML.

la définition de XML est assez claire au sujet du nom de l'élément dans ses spécifications (Langage de Balisage Extensible (XML) 1.0 (Cinquième Édition)):

[4]  NameStartChar  ::=   ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
[4a] NameChar       ::=   NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
[5]  Name           ::=   NameStartChar (NameChar)*

cette notation peut être transposée dans une expression régulière compatible UTF-8 à utiliser avec preg_match, ici comme chaîne PHP à copier mot pour mot:

'~^[:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\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}][:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\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}.\-0-9\xB7\x{0300}-\x{036F}\x{203F}-\x{2040}]*$~u'

Ou une autre variante nommée sous-masques dans un plus lisible de la mode:

'~
# XML 1.0 Name symbol PHP PCRE regex <http://www.w3.org/TR/REC-xml/#NT-Name>
(?(DEFINE)
    (?<NameStartChar> [:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\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}])
    (?<NameChar>      (?&NameStartChar) | [.\-0-9\xB7\x{0300}-\x{036F}\x{203F}-\x{2040}])
    (?<Name>          (?&NameStartChar) (?&NameChar)*)
)
^(?&Name)$
~ux'

notez que ce motif contient le colon : que vous pourriez vouloir exclure (deux appareances dans le premier modèle, une dans le second) pour des raisons de validation D'espace de noms XML (par exemple un test pour NCName).

Exemple D'Utilisation:

$name    = '::...';
$pattern = '~
# XML 1.0 Name symbol PHP PCRE regex <http://www.w3.org/TR/REC-xml/#NT-Name>
(?(DEFINE)
    (?<NameStartChar> [:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\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}])
    (?<NameChar>      (?&NameStartChar) | [.\-0-9\xB7\x{0300}-\x{036F}\x{203F}-\x{2040}])
    (?<Name>          (?&NameStartChar) (?&NameChar)*)
)
^(?&Name)$
~ux';

$valid = 1 === preg_match($pattern, $name); # bool(true)

Le dicton qu'un nom d'élément commençant par XML (en lettres minuscules ou en majuscules) n'est pas correct. <XML/> est un XML parfaitement bien formé et XML est un nom d'élément parfaitement bien formé.

c'est juste que ces noms sont dans le sous-ensemble des noms d'éléments bien formés qui sont réservé à la normalisation (version XML 1.0 et supérieure). Il est facile de tester si un (bien formé) nom de l'élément est réservé avec une chaîne comparaison:

$reserved = $valid && 0 === stripos($name, 'xml'));

ou alternativement une autre expression régulière:

$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);

PHP's DOMDocument test pour les noms réservés au moins je ne sais pas comment faire ça et j'ai beaucoup cherché.

un nom d'élément valide a besoin d'un Déclaration De Type D'Élément Unique ce qui semble hors du champ de la question ici puisqu'Aucune déclaration de ce type n'a été fournie. Donc la réponse n' prendre soin de cela. S'il y avait une déclaration de type d'élément, vous n'auriez qu'à valider par rapport à une liste blanche de tous les noms (sensibles à la casse), ce serait donc une simple comparaison de chaîne de caractères sensible à la casse.


Excursion: Ce n' DOMDocument faire différent de l'Expression Régulière?

En comparaison avec un DOMDocument/DOMElement, il y a quelques différences ce qui qualifie un nom d'élément valide. L'extension DOM est en quelque sorte de mode mixte qui le rend moins prévisible ce qu'il valide. L'excursion suivante illustre le comportement et montre comment le contrôler.

prenons $name et instancier un élément:

$element = new DOMElement($name);

Le résultat dépend:

  • si le premier caractère est deux points, il valide simplement le XML 1.0 .
  • si le premier caractère n'est pas deux points, il valide le xmlns 1.0 QName symbole

ainsi le premier personnage décide du mode de comparaison.

Une expression régulière est écrit spécifiquement ce que pour vérifier, voici le XML 1.0 Name symbole.

Vous pouvez réaliser la même chose avec DOMElement en préfixant le nom par une virgule:

function isValidXmlName($name)
{

    try {
        new DOMElement(":$name");
        return TRUE;
    } catch (DOMException $e) {
        return FALSE;
    }
}

pour vérifier explicitement le QName cela peut être réalisé en le transformant en un PrefixedName dans le cas d'un UnprefixedName:

function isValidXmlnsQname($qname)
{
    $prefixedName = (!strpos($qname, ':') ? 'prefix:' : '') . $qname;

    try {
        new DOMElement($prefixedName, NULL, 'uri:ns');
        return TRUE;
    } catch (DOMException $e) {
        return FALSE;
    }
}
14
répondu hakre 2013-03-04 12:17:36

Inspiré par le mef belle réponse, mais avec des et se terminant '$' (sinon XML des noms contenant des espaces comme "aaa bbb" seront acceptés).

$validXmlName = (preg_match('/^(?!XML)[a-z][\w0-9-]*$/i', $subject) != 0);
1
répondu Frosty Z 2010-10-27 10:33:23

Utiliser cette regex:

^_?(?!(xml / [_\D\W]) ([\W. -]+)$

cela correspond à tous vos quatre points et permet des caractères unicode.

0
répondu Timo Zimmermann 2012-05-04 10:45:53

si vous utilisez le framework DotNet, essayez XmlConvert.VerifyName. Il vous indiquera si le nom est valide, ou utilisez XmlConvert.EncodeName pour convertir un nom invalide en un nom valide...

0
répondu Keith Vinson 2012-08-07 16:42:33

l'expression ci-dessous doit correspondre aux noms d'éléments unicode valides sauf xml. Les noms qui commencent ou se terminent par xml seront toujours autorisés. Cela réussit le test d'äøñ de @toscho. La seule chose pour laquelle je n'ai pas pu trouver de regex était les extenseurs. Le nom de l'élément xml spec dit:

[4] NameChar ::= Lettre | Chiffre | '.'| '-' | '_' | ':' | Combinechar / Extender

[5] Nom :: = (Lettre| '_' | ':') (NameChar)*

mais il n'y a pas de clair définition d'une catégorie ou d'une classe unicode contenant des extendeurs.

^[\p{L}_:][\p{N}\p{L}\p{Mc}.\-|:]*((?<!xml)|xml)$
0
répondu JamieSee 2012-08-20 17:35:24

XML, xml et etc sont des balises valides, elles sont juste "réservées à la standardisation dans cette version ou dans les versions futures de cette spécification" ce qui ne se produira probablement jamais. S'il vous plaît vérifier la norme réelle à https://www.w3.org/TR/REC-xml/