diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md
index 9dd5555796..b54e3498e6 100644
--- a/CHANGELOG-2.1.md
+++ b/CHANGELOG-2.1.md
@@ -139,6 +139,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
### DomCrawler
+ * refactor the Form class internals to support multi-dimensional fields (the public API is backward compatible)
* added a way to get parsing errors for Crawler::addHtmlContent() and Crawler::addXmlContent() via libxml functions
* added support for submitting a form without a submit button
@@ -212,7 +213,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
### Serializer
* [BC BREAK] changed `GetSetMethodNormalizer`'s key names from all lowercased to camelCased (e.g. `mypropertyvalue` to `myPropertyValue`)
- * [BC BREAK] convert the `item` XML tag to an array
+ * [BC BREAK] convert the `item` XML tag to an array
``` xml
diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php
index 3ef7299d72..a235f953cc 100644
--- a/src/Symfony/Component/DomCrawler/Form.php
+++ b/src/Symfony/Component/DomCrawler/Form.php
@@ -63,7 +63,7 @@ class Form extends Link implements \ArrayAccess
public function setValues(array $values)
{
foreach ($values as $name => $value) {
- $this[$name] = $value;
+ $this->fields->set($name, $value);
}
return $this;
@@ -81,7 +81,7 @@ class Form extends Link implements \ArrayAccess
public function getValues()
{
$values = array();
- foreach ($this->fields as $name => $field) {
+ foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
continue;
}
@@ -108,7 +108,8 @@ class Form extends Link implements \ArrayAccess
}
$files = array();
- foreach ($this->fields as $name => $field) {
+
+ foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
continue;
}
@@ -124,7 +125,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets the field values as PHP.
*
- * This method converts fields with th array notation
+ * This method converts fields with the array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values.
@@ -142,7 +143,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets the file field values as PHP.
*
- * This method converts fields with th array notation
+ * This method converts fields with the array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values.
@@ -214,7 +215,7 @@ class Form extends Link implements \ArrayAccess
*/
public function has($name)
{
- return isset($this->fields[$name]);
+ return $this->fields->has($name);
}
/**
@@ -222,11 +223,13 @@ class Form extends Link implements \ArrayAccess
*
* @param string $name The field name
*
+ * @throws \InvalidArgumentException when the name is malformed
+ *
* @api
*/
public function remove($name)
{
- unset($this->fields[$name]);
+ $this->fields->remove($name);
}
/**
@@ -242,25 +245,21 @@ class Form extends Link implements \ArrayAccess
*/
public function get($name)
{
- if (!$this->has($name)) {
- throw new \InvalidArgumentException(sprintf('The form has no "%s" field', $name));
- }
-
- return $this->fields[$name];
+ return $this->fields->get($name);
}
/**
* Sets a named field.
*
- * @param Field\FormField $field The field
+ * @param FormField $field The field
*
* @return FormField The field instance
*
* @api
*/
- public function set(Field\FormField $field)
+ public function set(FormField $field)
{
- $this->fields[$field->getName()] = $field;
+ $this->fields->add($field);
}
/**
@@ -272,12 +271,78 @@ class Form extends Link implements \ArrayAccess
*/
public function all()
{
- return $this->fields;
+ return $this->fields->all();
+ }
+
+ /**
+ * Returns true if the named field exists.
+ *
+ * @param string $name The field name
+ *
+ * @return Boolean true if the field exists, false otherwise
+ */
+ public function offsetExists($name)
+ {
+ return $this->has($name);
+ }
+
+ /**
+ * Gets the value of a field.
+ *
+ * @param string $name The field name
+ *
+ * @return FormField The associated Field instance
+ *
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function offsetGet($name)
+ {
+ return $this->fields->get($name);
+ }
+
+ /**
+ * Sets the value of a field.
+ *
+ * @param string $name The field name
+ * @param string|array $value The value of the field
+ *
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function offsetSet($name, $value)
+ {
+ $this->fields->set($name, $value);
+ }
+
+ /**
+ * Removes a field from the form.
+ *
+ * @param string $name The field name
+ */
+ public function offsetUnset($name)
+ {
+ $this->fields->remove($name);
+ }
+
+ protected function setNode(\DOMNode $node)
+ {
+ $this->button = $node;
+ if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
+ do {
+ // use the ancestor form element
+ if (null === $node = $node->parentNode) {
+ throw new \LogicException('The selected node does not have a form ancestor.');
+ }
+ } while ('form' != $node->nodeName);
+ } elseif('form' != $node->nodeName) {
+ throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
+ }
+
+ $this->node = $node;
}
private function initialize()
{
- $this->fields = array();
+ $this->fields = new FormFieldRegistry();
$document = new \DOMDocument('1.0', 'UTF-8');
$node = $document->importNode($this->node, true);
@@ -313,78 +378,202 @@ class Form extends Link implements \ArrayAccess
}
}
}
-
- /**
- * Returns true if the named field exists.
- *
- * @param string $name The field name
- *
- * @return Boolean true if the field exists, false otherwise
- */
- public function offsetExists($name)
- {
- return $this->has($name);
- }
-
- /**
- * Gets the value of a field.
- *
- * @param string $name The field name
- *
- * @return FormField The associated Field instance
- *
- * @throws \InvalidArgumentException if the field does not exist
- */
- public function offsetGet($name)
- {
- if (!$this->has($name)) {
- throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
- }
-
- return $this->fields[$name];
- }
-
- /**
- * Sets the value of a field.
- *
- * @param string $name The field name
- * @param string|array $value The value of the field
- *
- * @throws \InvalidArgumentException if the field does not exist
- */
- public function offsetSet($name, $value)
- {
- if (!$this->has($name)) {
- throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
- }
-
- $this->fields[$name]->setValue($value);
- }
-
- /**
- * Removes a field from the form.
- *
- * @param string $name The field name
- */
- public function offsetUnset($name)
- {
- $this->remove($name);
- }
-
- protected function setNode(\DOMNode $node)
- {
- $this->button = $node;
- if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
- do {
- // use the ancestor form element
- if (null === $node = $node->parentNode) {
- throw new \LogicException('The selected node does not have a form ancestor.');
- }
- } while ('form' != $node->nodeName);
- } elseif('form' != $node->nodeName) {
- throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
- }
-
- $this->node = $node;
- }
}
+
+class FormFieldRegistry
+{
+ private $fields = array();
+
+ private $base;
+
+ /**
+ * Adds a field to the registry.
+ *
+ * @param FormField $field The field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ public function add(FormField $field)
+ {
+ $segments = $this->getSegments($field->getName());
+
+ $target =& $this->fields;
+ while ($segments) {
+ if (!is_array($target)) {
+ $target = array();
+ }
+ $path = array_shift($segments);
+ if ('' === $path) {
+ $target =& $target[];
+ } else {
+ $target =& $target[$path];
+ }
+ }
+ $target = $field;
+ }
+
+ /**
+ * Removes a field and its children from the registry.
+ *
+ * @param string $name The fully qualified name of the base field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ public function remove($name)
+ {
+ $segments = $this->getSegments($name);
+ $target =& $this->fields;
+ while (count($segments) > 1) {
+ $path = array_shift($segments);
+ if (!array_key_exists($path, $target)) {
+ return;
+ }
+ $target =& $target[$path];
+ }
+ unset($target[array_shift($segments)]);
+ }
+
+ /**
+ * Returns the value of the field and its children.
+ *
+ * @param string $name The fully qualified name of the field
+ *
+ * @return mixed The value of the field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function &get($name)
+ {
+ $segments = $this->getSegments($name);
+ $target =& $this->fields;
+ while ($segments) {
+ $path = array_shift($segments);
+ if (!array_key_exists($path, $target)) {
+ throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
+ }
+ $target =& $target[$path];
+ }
+
+ return $target;
+ }
+
+ /**
+ * Tests whether the form has the given field.
+ *
+ * @param string $name The fully qualified name of the field
+ *
+ * @return Boolean Whether the form has the given field
+ */
+ public function has($name)
+ {
+ try {
+ $this->get($name);
+ return true;
+ } catch (\InvalidArgumentException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Set the value of a field and its children.
+ *
+ * @param string $name The fully qualified name of the field
+ * @param mixed $value The value
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function set($name, $value)
+ {
+ $target =& $this->get($name);
+ if (is_array($value)) {
+ $fields = self::create($name, $value);
+ foreach ($fields->all() as $k => $v) {
+ $this->set($k, $v);
+ }
+ } else {
+ $target->setValue($value);
+ }
+ }
+
+ /**
+ * Returns the list of field with their value.
+ *
+ * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+ */
+ public function all()
+ {
+ return $this->walk($this->fields, $this->base);
+ }
+
+ /**
+ * Creates an instance of the class.
+ *
+ * This function is made private because it allows overriding the $base and
+ * the $values properties without any type checking.
+ *
+ * @param string $base The fully qualified name of the base field
+ * @param array $values The values of the fields
+ *
+ * @return FormFieldRegistry
+ */
+ static private function create($base, array $values)
+ {
+ $registry = new static();
+ $registry->base = $base;
+ $registry->fields = $values;
+
+ return $registry;
+ }
+
+ /**
+ * Transforms a PHP array in a list of fully qualified name / value.
+ *
+ * @param array $array The PHP array
+ * @param string $base The name of the base field
+ * @param array $output The initial values
+ *
+ * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+ */
+ private function walk(array $array, $base = '', array &$output = array())
+ {
+ foreach ($array as $k => $v) {
+ $path = empty($base) ? $k : sprintf("%s[%s]", $base, $k);
+ if (is_array($v)) {
+ $this->walk($v, $path, $output);
+ } else {
+ $output[$path] = $v;
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Splits a field name into segments as a web browser would do.
+ *
+ *
+ * getSegments('base[foo][3][]') = array('base', 'foo, '3', '');
+ *
+ *
+ * @param string $name The name of the field
+ *
+ * @return array The list of segments
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ private function getSegments($name)
+ {
+ if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) {
+ $segments = array($m['base']);
+ while (preg_match('/^\[(?P.*?)\](?P.*)$/', $m['extra'], $m)) {
+ $segments[] = $m['segment'];
+ }
+
+ return $segments;
+ }
+
+ throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
+ }
+}
\ No newline at end of file
diff --git a/tests/Symfony/Tests/Component/DomCrawler/FormTest.php b/tests/Symfony/Tests/Component/DomCrawler/FormTest.php
index a1ae49a090..01df4444b5 100644
--- a/tests/Symfony/Tests/Component/DomCrawler/FormTest.php
+++ b/tests/Symfony/Tests/Component/DomCrawler/FormTest.php
@@ -12,9 +12,17 @@
namespace Symfony\Tests\Component\DomCrawler;
use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\DomCrawler\FormFieldRegistry;
+use Symfony\Component\DomCrawler\Field;
class FormTest extends \PHPUnit_Framework_TestCase
{
+ public static function setUpBeforeClass()
+ {
+ // Ensure that the private helper class FormFieldRegistry is loaded
+ class_exists('Symfony\\Component\\DomCrawler\\Form');
+ }
+
public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
{
$dom = new \DOMDocument();
@@ -48,19 +56,64 @@ class FormTest extends \PHPUnit_Framework_TestCase
try {
$form = new Form($nodes->item(0), 'http://example.com');
- $this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
+ $this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
} catch (\LogicException $e) {
- $this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
+ $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
}
}
+ public function testMultiValuedFields()
+ {
+ $form = $this->createForm('
+ ');
+
+ $this->assertEquals(
+ array_keys($form->all()),
+ array('foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]')
+ );
+
+ $this->assertEquals($form->get('foo[2]')->getValue(), 'foo');
+ $this->assertEquals($form->get('foo[3]')->getValue(), 'foo');
+ $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo');
+ $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo');
+
+ $form['foo[2]'] = 'bar';
+ $form['foo[3]'] = 'bar';
+
+ $this->assertEquals($form->get('foo[2]')->getValue(), 'bar');
+ $this->assertEquals($form->get('foo[3]')->getValue(), 'bar');
+
+ $form['bar'] = array('foo' => array('0' => 'bar', 'foobar' => 'foobar'));
+
+ $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar');
+ $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar');
+
+ }
+
/**
* @dataProvider provideInitializeValues
*/
public function testConstructor($message, $form, $values)
{
$form = $this->createForm('');
- $this->assertEquals($values, array_map(function ($field) { return array(get_class($field), $field->getValue()); }, $form->all()), '->getDefaultValues() '.$message);
+ $this->assertEquals(
+ $values,
+ array_map(function ($field) {
+ $class = get_class($field);
+ return array(substr($class, strrpos($class, '\\') + 1), $field->getValue());
+ },
+ $form->all()
+ ),
+ '->getDefaultValues() '.$message
+ );
}
public function provideInitializeValues()
@@ -76,55 +129,55 @@ class FormTest extends \PHPUnit_Framework_TestCase
'takes into account disabled input fields',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\InputFormField', 'foo')),
+ array('foo' => array('InputFormField', 'foo')),
),
array(
'appends the submitted button value',
'',
- array('bar' => array('Symfony\\Component\\DomCrawler\\Field\\InputFormField', 'bar')),
+ array('bar' => array('InputFormField', 'bar')),
),
array(
'appends the submitted button value but not other submit buttons',
'
',
- array('foobar' => array('Symfony\\Component\\DomCrawler\\Field\\InputFormField', 'foobar')),
+ array('foobar' => array('InputFormField', 'foobar')),
),
array(
'returns textareas',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\TextareaFormField', 'foo')),
+ array('foo' => array('TextareaFormField', 'foo')),
),
array(
'returns inputs',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\InputFormField', 'foo')),
+ array('foo' => array('InputFormField', 'foo')),
),
array(
'returns checkboxes',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\ChoiceFormField', 'foo')),
+ array('foo' => array('ChoiceFormField', 'foo')),
),
array(
'returns not-checked checkboxes',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\ChoiceFormField', false)),
+ array('foo' => array('ChoiceFormField', false)),
),
array(
'returns radio buttons',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\ChoiceFormField', 'bar')),
+ array('foo' => array('ChoiceFormField', 'bar')),
),
array(
'returns file inputs',
'
',
- array('foo' => array('Symfony\\Component\\DomCrawler\\Field\\FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
+ array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
),
);
}
@@ -248,6 +301,23 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message);
}
+ public function testGetBaseUri()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadHTML('');
+
+ $nodes = $dom->getElementsByTagName('input');
+ $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/');
+ $this->assertEquals('http://www.foo.com/foo.php', $form->getUri());
+ }
+
+ public function testGetUriWithAnchor()
+ {
+ $form = $this->createForm('', null, 'http://example.com/id/123');
+
+ $this->assertEquals('http://example.com/id/123#foo', $form->getUri());
+ }
+
public function testGetUriActionAbsolute()
{
$formHtml='';
@@ -382,23 +452,6 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Symfony\\Component\\DomCrawler\\Field\\InputFormField', get_class($fields['bar']), '->all() return an array of form field objects');
}
- public function testBase()
- {
- $dom = new \DOMDocument();
- $dom->loadHTML('');
-
- $nodes = $dom->getElementsByTagName('input');
- $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/');
- $this->assertEquals('http://www.foo.com/foo.php', $form->getUri());
- }
-
- public function testUriWithAnchor()
- {
- $form = $this->createForm('', null, 'http://example.com/id/123');
-
- $this->assertEquals('http://example.com/id/123#foo', $form->getUri());
- }
-
public function testSubmitWithoutAFormButton()
{
$dom = new \DOMDocument();
@@ -415,6 +468,156 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
}
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->add($this->getFormFieldMock('[foo]'));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->remove('[foo]');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->get('[foo]');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->get('foo');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->set('[foo]', null);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->set('foo', null);
+ }
+
+ public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->add($this->getFormFieldMock('foo[bar]'));
+
+ $this->assertTrue($registry->has('foo'));
+ $this->assertTrue($registry->has('foo[bar]'));
+ $this->assertFalse($registry->has('bar'));
+ $this->assertFalse($registry->has('foo[foo]'));
+ }
+
+ public function testFormRegistryFieldsCanBeRemoved()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->add($this->getFormFieldMock('foo'));
+ $registry->remove('foo');
+ $this->assertFalse($registry->has('foo'));
+ }
+
+ public function testFormRegistrySupportsMultivaluedFields()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->add($this->getFormFieldMock('foo[]'));
+ $registry->add($this->getFormFieldMock('foo[]'));
+ $registry->add($this->getFormFieldMock('bar[5]'));
+ $registry->add($this->getFormFieldMock('bar[]'));
+ $registry->add($this->getFormFieldMock('bar[baz]'));
+
+ $this->assertEquals(
+ array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'),
+ array_keys($registry->all())
+ );
+ }
+
+ public function testFormRegistrySetValues()
+ {
+ $registry = new FormFieldRegistry();
+ $registry->add($f2 = $this->getFormFieldMock('foo[2]'));
+ $registry->add($f3 = $this->getFormFieldMock('foo[3]'));
+ $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]'));
+
+ $f2
+ ->expects($this->exactly(2))
+ ->method('setValue')
+ ->with(2)
+ ;
+
+ $f3
+ ->expects($this->exactly(2))
+ ->method('setValue')
+ ->with(3)
+ ;
+
+ $fbb
+ ->expects($this->exactly(2))
+ ->method('setValue')
+ ->with('fbb')
+ ;
+
+ $registry->set('foo[2]', 2);
+ $registry->set('foo[3]', 3);
+ $registry->set('foo[bar][baz]', 'fbb');
+
+ $registry->set('foo', array(
+ 2 => 2,
+ 3 => 3,
+ 'bar' => array(
+ 'baz' => 'fbb'
+ )
+ ));
+ }
+
+ protected function getFormFieldMock($name, $value = null)
+ {
+ $field = $this
+ ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField')
+ ->setMethods(array('getName', 'getValue', 'setValue', 'initialize'))
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+
+ $field
+ ->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue($name))
+ ;
+
+ $field
+ ->expects($this->any())
+ ->method('getValue')
+ ->will($this->returnValue($value))
+ ;
+
+ return $field;
+ }
+
protected function createForm($form, $method = null, $currentUri = null)
{
$dom = new \DOMDocument();