Это было упущено до сих пор, несмотря на тот факт, что вопрос старый: проверка имени с помощью PHP-функций pcre, которые оптимизированы с помощью спецификации XML.
Определение XML довольно ясно о названии элемента в его спецификациях ( Расширяемый язык разметки (XML) 1.0 (Пятое издание) ):
[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)*
Эта запись может быть преобразована в регулярное выражение, совместимое с UTF-8, для использования с preg_match
, здесь как строка PHP в одинарных кавычках для дословного копирования:
'~^[: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'
Или как другой вариант с именованными подшаблонами в более читабельной форме:
'~
# 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'
Обратите внимание, что этот шаблон содержит двоеточие :
, которое вы, возможно, захотите исключить (два вхождения в первом шаблоне, один во втором) по причинам проверки пространства имен XML (например, тест для NCName
). * * тысяча двадцать-один
Пример использования:
$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)
Поговорка о том, что имя элемента, начинающееся с XML
(строчными или заглавными буквами), была бы невозможна, неверна. <XML/>
- идеально правильно сформированный XML, а XML
- идеально правильно сформированное имя элемента.
Просто такие имена находятся в подмножестве правильно сформированных имен элементов, которые зарезервированы для стандартизации (версия XML 1.0 и выше). Легко проверить, зарезервировано ли (правильно сформированное) имя элемента с помощью сравнения строк:
$reserved = $valid && 0 === stripos($name, 'xml'));
или, альтернативно, другое регулярное выражение:
$reserved = $valid && 1 === preg_match('~^[Xx][Mm][Ll]~', $name);
PHP DOMDocument
может не проверить для зарезервированных имен, по крайней мере, я не знаю, как это сделать, и я много искал.
Для действительного имени элемента требуется Уникальное объявление типа элемента , которое, по-видимому, выходит за рамки данного вопроса, поскольку такое объявление не было предоставлено. Поэтому ответ не заботится об этом. Если бы существовало объявление типа элемента, вам нужно было бы проверить только по белому списку всех (чувствительных к регистру) имен, так что это будет простое сравнение строк с учетом регистра.
Экскурсия: Чем DOMDocument
отличается от регулярного выражения?
По сравнению с DOMDocument
/ DOMElement
есть некоторые различия в том, что относится к допустимому имени элемента. Расширение DOM находится в некотором смешанном режиме, который делает его менее предсказуемым, что он проверяет. Следующая экскурсия иллюстрирует поведение и показывает, как его контролировать.
Давайте возьмем $name
и создадим элемент:
$element = new DOMElement($name);
Результат зависит от:
Итак, первый персонаж решает режим сравнения.
Регулярное выражение специально написано, что проверять, здесь символ XML 1.0 Name
.
Вы можете добиться того же с DOMElement
, поставив перед двоеточием имя:
function isValidXmlName($name)
{
try {
new DOMElement(":$name");
return TRUE;
} catch (DOMException $e) {
return FALSE;
}
}
Чтобы явно проверить QName
, этого можно добиться, превратив его в PrefixedName
, если это UnprefixedName
:
function isValidXmlnsQname($qname)
{
$prefixedName = (!strpos($qname, ':') ? 'prefix:' : '') . $qname;
try {
new DOMElement($prefixedName, NULL, 'uri:ns');
return TRUE;
} catch (DOMException $e) {
return FALSE;
}
}