Php 如何区分表示元素和属性的SimpleXML对象?

Php 如何区分表示元素和属性的SimpleXML对象?,php,reflection,simplexml,Php,Reflection,Simplexml,我需要以特定的方式打印任意SimpleXML对象,并对属性节点进行特殊处理 问题在于SimpleXML元素和属性似乎使用完全相同的类,属性节点甚至假装支持attributes()方法,而SimpleXML隐藏了其内部,因此似乎没有任何方法来判断节点的类型(除了生成XML和重新分析它) 两者给出相同的结果: $element=新的SimpleXMLElement('test'); echo$元素; 打印(元素); $element=新的SimpleXMLElement(“”); echo$elem

我需要以特定的方式打印任意SimpleXML对象,并对属性节点进行特殊处理

问题在于SimpleXML元素和属性似乎使用完全相同的类,属性节点甚至假装支持
attributes()
方法,而SimpleXML隐藏了其内部,因此似乎没有任何方法来判断节点的类型(除了生成XML和重新分析它)

两者给出相同的结果:

$element=新的SimpleXMLElement('test');
echo$元素;
打印(元素);
$element=新的SimpleXMLElement(“”);
echo$element['attr'];
打印($element['attr']);
是否存在允许标识SimpleXML中节点类型的隐藏属性/方法?等同于DOM的
$node->nodeType
$node instanceof DOMAttr
?(我不能改用DOM,对SimpleXML的支持是核心要求)。

您需要:

函数xml\u out($el){
$name=$el->getName();
回声“”(字符串)$el;
如果(计数($el->children()){
foreach($el->children()作为$c){
xml_out($c);
}
}
回声';
}

可能需要一些调整。

不幸的是,没有隐藏的属性或方法允许识别SimpleXML中的节点类型。SimpleXML只使用一个类,元素没有任何指向其父类的内容。如果您尝试下面的反射,您将看到没有什么可以区分元素或属性

$element = new SimpleXMLElement('<foo>test</foo>');
echo ReflectionObject::export($element);

$element = new SimpleXMLElement('<foo attr="test">test</foo>');
echo ReflectionObject::export($element['attr']);
$element=新的SimpleXMLElement('test');
回波反射对象::导出($element);
$element=新的SimpleXMLElement(“测试”);
回波反射对象::导出($element['attr']);
但是,如果元素具有属性,则可以检测该属性。因此,您必须假设传入的所有元素都具有属性。有了这个假设,你就可以把它们区分开来

$element = new SimpleXMLElement('<foo attr="test">test</foo>');

echo ReflectionObject::export($element);
echo ReflectionObject::export($element['attr']);
$element=新的SimpleXMLElement('test');
回波反射对象::导出($element);
回波反射对象::导出($element['attr']);
这是我能想到的最好的了。请记住,没有属性的元素可能仍然返回false

function isNotAttribute($simpleXML)
{
  return (count($simpleXML->attributes()) > 0);
}

$element = new SimpleXMLElement('<foo attr="test">test</foo>');
var_dump(isNotAttribute($element));
var_dump(isNotAttribute($element['attr']));

// returns
bool(true)
bool(false)
函数不是属性($simpleXML)
{
返回(计数($simpleXML->attributes())>0);
}
$element=新的SimpleXMLElement(“测试”);
var_dump(isNotAttribute($element));
变量转储(isNotAttribute($element['attr']);
//返回
布尔(真)
布尔(假)

是的,有办法。好的,您可以通过API检索到任何优雅的东西,但是SimpleXML的内部某处正在跟踪它是什么,并且它会产生差异,例如,当您调用诸如getName()或asXML()之类的函数时

$element=新的SimpleXMLElement('test');
打印($element->getName());
打印($element->asXML());
回显“--------------\n”;
$element=新的SimpleXMLElement(“”);
$at=$element['attr'];
打印($at->getName());
打印($at->asXML());
福
测试
------------------
属性
attr=“测试”

你的代码不会赢得选美比赛,但至少你可以做到。

用palako指出的方法,这样的函数可能会起作用:

function is_attribute($node) {
    return !($node->asXML()[0] == "<")
}
函数是_属性($node){
return!($node->asXML()[0]=”
是否有一个隐藏属性/方法允许识别SimpleXML中的节点类型?相当于DOM的$node->nodeType或$node instanceof DOMAttr?(我不能改用DOM,对SimpleXML的支持是核心要求)

答案是否定的……而且是肯定的。SimpleXML没有这样的属性,但有一个好消息:SimpleXML和DOM是同一个硬币的两面。(硬币是libxml;)您不必选择一个或另一个,两个都可以使用!您可以将
simplexmlement
转换为
DOMNode
,反之亦然。在您的情况下,您可以执行以下操作:

$element = new SimpleXMLElement('<foo attr="test" />');
$is_attr = (dom_import_simplexml($element['attr'])->nodeType === XML_ATTRIBUTE_NODE);
$element=新的SimpleXMLElement(“”);
$is_attr=(dom_import_simplexml($element['attr'])->nodeType==XML\u ATTRIBUTE\u NODE);
如果这是您经常做的事情,或者您不想处理DOM/SimpleXML杂耍,那么您可以看看

$element=newsimpledom(“”);
$is_attr=($element['attr']->nodeType()==XML_ATTRIBUTE_NODE);

SimpleXMLElement
中没有允许您区分这些属性的内置属性

但是,正如其他人所建议的那样,该函数有时可以动态更改节点,例如,如果您传入一个ChildNode或命名的ChildNode列表,它将获取这些节点并将它们转换为第一个元素

如果它是一个空列表,例如,没有从
attributes()
返回的属性或不存在的命名子节点,它将发出警告,告诉您提供了无效的节点类型:

警告:dom\u import\u simplexml():要导入的节点类型无效

因此,如果您需要一个简洁的布尔值
true
/
false
,下面是它如何与Simplexml一起工作的:

$isElement   = $element->xpath('.') == array($element);

$isAttribute = $element[0] == $element
               and $element->xpath('.') != array($element);
它的工作原理与属性列表和元素列表类似,我已经,你需要有一些具体的知识来评估什么,所以我为它创建了一个备忘表:

+------------------+---------------------------------------------+
| TYPE             | TEST                                        |
+------------------+---------------------------------------------+
| Element          | $element->xpath('.') == array($element)     |
+------------------+---------------------------------------------+
| Attribute        | $element[0] == $element                     |
|                  | and $element->xpath('.') != array($element) |
+------------------+---------------------------------------------+
| Attributes       | $element->attributes() === NULL             |
+------------------+---------------------------------------------+
| Elements         | $element[0] != $element                     |
|                  | and $element->attributes() !== NULL         |
+------------------+---------------------------------------------+
| Single           | $element[0] == $element                     |
+------------------+---------------------------------------------+
| Empty List       | $element[0] == NULL                         |
+------------------+---------------------------------------------+
| Document Element | $element->xpath('/*') == array($element)    |
+------------------+---------------------------------------------+

不幸的是,这假设我的代码将从一开始就获取元素。我无法控制函数的输入是元素还是属性。您还可以通过->children()检查元素是否有子元素如果元素有任何属性或子元素,则它不是属性。嘿,gradbot,建议的isNotAttribute函数不起作用。对于没有属性的元素,它将返回false。顺便说一句,以这种方式命名函数(isNotX)强烈不推荐:)是的,palako,我在上面说过,如果元素没有属性,它将失败。
$element = new SimpleXMLElement('<foo attr="test" />');
$is_attr = (dom_import_simplexml($element['attr'])->nodeType === XML_ATTRIBUTE_NODE);
$element = new SimpleDOM('<foo attr="test" />');
$is_attr = ($element['attr']->nodeType() === XML_ATTRIBUTE_NODE);
$isElement   = $element->xpath('.') == array($element);

$isAttribute = $element[0] == $element
               and $element->xpath('.') != array($element);
+------------------+---------------------------------------------+
| TYPE             | TEST                                        |
+------------------+---------------------------------------------+
| Element          | $element->xpath('.') == array($element)     |
+------------------+---------------------------------------------+
| Attribute        | $element[0] == $element                     |
|                  | and $element->xpath('.') != array($element) |
+------------------+---------------------------------------------+
| Attributes       | $element->attributes() === NULL             |
+------------------+---------------------------------------------+
| Elements         | $element[0] != $element                     |
|                  | and $element->attributes() !== NULL         |
+------------------+---------------------------------------------+
| Single           | $element[0] == $element                     |
+------------------+---------------------------------------------+
| Empty List       | $element[0] == NULL                         |
+------------------+---------------------------------------------+
| Document Element | $element->xpath('/*') == array($element)    |
+------------------+---------------------------------------------+