Notes On XML
XML Comments
Comments in XML is the same as with HTML: <!-- THIS IS A COMMENT -->.
CDATA
In an XML document or external entity, a CDATA section is a piece of element content
that is marked up to be interpreted literally, as textual data, not as marked-up content.
A CDATA section is merely an alternative syntax for expressing character data; there is
no semantic difference between character data in a CDATA section and character data in
standard syntax where, for example, "<" and "&" are represented by "<" and
"&", respectively.
A CDATA section starts with <![CDATA[ and ends with ]]> all of the
characters between these tags are interpreted as characters, not markup or entity
references. Every character is taken literally, the only exception being the ]]>
sequence of characters. In:
<sender>John Smith</sender>
the start and end sender tags are interpreted as markup.
However, the code:
<![CDATA[<sender>John Smith</sender>]]>
is equivalent to:
<sender>John Smith</sender>
Thus, the "tags" will have exactly the same status as the "John Smith"; they will be treated as text.
Use Of CDATA
Sometimes the data you need to store contains characters that would cause the interpreter to fail, such as
the use of HTML tags.
PHP XML Classes
There are two primary classes that are used to deal with XML within the PHP language.
- SimpleXML - Works well for simple XML operations like reading and retrieval, but can fall short when it comes to more complex XML manipulation.
- DOM - A much more comprehensive class that, while often more difficult to use, can accomplish most anything needed to maipulate XML.
XML Data
The following is an example of what a login XML file might look like.
<?xml version="1.0" encoding="utf-8"?>
<users>
<user>
<user_id>0</user_id>
<user_status>1</user_status>
<user_name>Dirk</user_name>
<user_pass><![CDATA[$2y$10$E2oUiMYBv4AmStGPKQdgaOp/rWajplyveC8IYU56FFIhwAX.jRFAa]]></user_pass>
<user_access>0</user_access>
</user>
<user>
<user_id>1</user_id>
<user_status>1</user_status>
<user_name>Jack</user_name>
<user_pass><![CDATA[$2y$10$obEgE/H6cyzDYfLYotUIIOaHM9WR7v5Aqu0fbrL5Z8OI.C1k4AD.2]]></user_pass>
<user_access>1</user_access>
</user>
<user>
<user_id>2</user_id>
<user_status>0</user_status>
<user_name>Sam</user_name>
<user_pass><![CDATA[$2y$10$3d8ABp1yuDbPNsC4aNeqYefTaQPCYyr.JhK0zuSpOOX2AFEIGKUo.]]></user_pass>
<user_access>1</user_access>
</user>
</users>
Simple XML
The SimpleXML extension provides a very simple and easily usable toolset to convert XML to an object
that can be processed with normal property selectors and array iterators.
The SimpleXMLElement class
class SimpleXMLElement implements Stringable, Countable, RecursiveIterator {
/* Methods */
public __construct(
string $data,
int $options = 0,
bool $dataIsURL = false,
string $namespaceOrPrefix = "",
bool $isPrefix = false
)
public addAttribute(string $qualifiedName, string $value, ?string $namespace = null): void
public addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null): ?SimpleXMLElement
public asXML(?string $filename = null): string|bool
public attributes(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement
public children(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement
public count(): int
public current(): SimpleXMLElement
public getDocNamespaces(bool $recursive = false, bool $fromRoot = true): array|false
public getName(): string
public getNamespaces(bool $recursive = false): array
public getChildren(): ?SimpleXMLElement
public hasChildren(): bool
public key(): string
public next(): void
public registerXPathNamespace(string $prefix, string $namespace): bool
public rewind(): void
public __toString(): string
public valid(): bool
public xpath(string $expression): array|null|false
}
Load XML with SimpleXML
simplexml_load_file(
string $filename,
?string $class_name = SimpleXMLElement::class,
int $options = 0,
string $namespace_or_prefix = "",
bool $is_prefix = false
): SimpleXMLElement|false
...
if (file_exists('login.xml')) {
$xml = simplexml_load_file("login.xml") or die ("Cannot open XML file");
} else {
// FILE DOES NOT EXIST
}
Simple XML - Accessing XML Data
In the following file a entered password is compared to a stored password.
The function used, password_verify(), checks the
<?php
// GET THE POSTED DATA
$txtUsername = $_POST['txtUsername'];
$txtPassword = $_POST['txtPassword'];
$error_str = '';
$info_str = '';
$access_level = -1;
/*
* XML STRUCTURE
*
* users
* user user_id status access
* user_name
* user_pass
*/
if (file_exists('xml_users.xml')) {
$xml = simplexml_load_file("users.xml") or die ("Cannot open XML file");
foreach($xml->children() as $users) {
if ($txtUsername == $users->user_name) {
if (password_verify($txtPassword, $users->user_pass)) {
// START A SESSION
session_start();
$access_level = (int)$users['access'];
$_SESSION['access'] = (int)$users['access'];
$_SESSION['sid'] = (int)$users['user_id'];
$_SESSION['user_name'] = $txtUsername;
break;
} else {
$error_str = "Incorrect Password";
break;
}
}
}
} else {
$error_str = "File Does Not Exist";
}
$output = array('errorString' => $errorStr,
'userName' => $txtUsername,
'accessLevel' => $access_level);
header('Content-Type:application/json');
echo json_encode($output, JSON_FORCE_OBJECT);
?>
Simple XML - Modifying XML Data
if (file_exists('login.xml')) {
$xml = simplexml_load_file("login.xml") or die ("Cannot open XML file");
foreach($xml->children() as $users) {
if ($username == $users->user_name) {
$users->user_status = 0;
break;
}
}
if ($xml->asXML('login.xml')) {
echo 'File Saved';
} else {
echo 'Unable to save to file';
}
} else {
// FILE DOES NOT EXIST
}
Writing CDATA Using Simple XML
Simple XML does not have a method for specifically writing CDATA.
A workaround is to create a class that extends the SimpleXMLElement.
class SimpleXMLExtended extends SimpleXMLElement {
public function addCData($cdata_text) {
$node= dom_import_simplexml($this);
$no = $node->ownerDocument;
$node->appendChild($no->createCDATASection($cdata_text));
}
}
Simple XML - Creating XML Data
<?php
class SimpleXMLExtended extends SimpleXMLElement {
public function addCData($cdata_text) {
$node= dom_import_simplexml($this);
$no = $node->ownerDocument;
$node->appendChild($no->createCDATASection($cdata_text));
}
}
/*
* XML STRUCTURE
*
* users
* user user_id status access
* user_pass (CDATA)
*
*/
if (file_exists('login.xml')) {
$xml_t = simplexml_load_file('xml_users.xml') or die ('Cannot open XML file');
$sxe = new SimpleXMLExtended($xml_t->asXML());
$user = $sxe->addChild('user');
$user->addAttribute('user_id', 5);
$user->addAttribute('status', 1);
$user->addAttribute('access', 2);
$user->user_pass = NULL;
$user->user_pass->addCData(password_hash($txtPassword, PASSWORD_DEFAULT));
$sxe->asXML('login.xml');
}
<?php
class SimpleXMLExtended extends SimpleXMLElement {
public function addCData($cdata_text) {
$node= dom_import_simplexml($this);
$no = $node->ownerDocument;
$node->appendChild($no->createCDATASection($cdata_text));
}
}
$txtUsername = $_POST['txtUsername'];
$txtPassword = $_POST['txtPassword'];
$info_str = '';
$access_level = 2;
$error_str = '';
$counter = 0;
/*
* users
* user user_id status access
* user_name
* user_pass
*/
if (file_exists('xml_users.xml')) {
$info_str = 'File Exists. ';
$xml_t = simplexml_load_file('xml_users.xml') or die ('Cannot open XML file');
$xml = new SimpleXMLExtended($xml_t->asXML());
// CHECK FOR EXISTING RECORD WITH ENTERED USERNAME
foreach($xml->children() as $users) {
$counter++;
if ($txtUsername == $users->user_name) {
$error_str = 'Error: Username Already Exists';
break; // EXIT THE FOR LOOP - NO NEED TO LOOK FURTHER
}
}
if ($error_str == "") {
$user = $xml->addChild('user');
$user->addAttribute('user_id',$counter);
$user->addAttribute('status','1'); // ACTIVE
$user->addAttribute('access','2'); // BASIC USER
// USER_NAME
$user->addChild('user_name', $txtUsername);
// USER_PASS - NEED TO WRITE AS CDATA
$user->user_pass = NULL;
$user->user_pass->addCData(password_hash($txtPassword, PASSWORD_DEFAULT));
// SAVE XML TO FILE
$xml->asXML('xml_users.xml');
$info_str .= 'User '. $txtUsername .' Added.';
}
} else {
$info_str = 'File Does Not Exist, Created';
/* FILE DOES NOT EXIST, SO CREATE XML TO BE SAVED TO FILE */
$domDoc = new DomDocument('1.0', 'UTF-8');
$domDoc->preserveWhiteSpace = false;
$domDoc->formatOutput = true;
// ADD ROOT ELEMENT
$users = $domDoc->appendChild($domDoc->createElement('users'));
// CREATE USER NODE
$user = $domDoc->createElement('user');
$user->setAttribute( 'user_id', '0' ); // FIRST USER
$user->setAttribute( 'status', '1' ); // ACTIVE
$user->setAttribute( 'access', '2' ); // BASIC USER
$user_name = $domDoc->createElement('user_name', $txtUsername);
$user_pass = $domDoc->createElement('user_pass'); // USER_PASS
$user_pass->appendChild($domDoc->createCDataSection(password_hash($txtPassword, PASSWORD_DEFAULT)));
// APPEND USER_NAME TO USER
$user->appendChild( $user_name );
// APPEND USER_PASS TO USER
$user->appendChild( $user_pass );
// APPEND USER TO USERS
$users->appendChild( $user );
// SAVE IN FILE "xml_users.xml"
$domDoc->save('xml_users.xml');
}
$output = array('errorString' => $error_str,
'infoString' => $info_str,
'userName' => $txtUsername,
'accessLevel' => $access_level);
header('Content-Type:application/json');
echo json_encode($output, JSON_FORCE_OBJECT);
?>
DOM
Load XML File
Accessing XML Data
Modifying XML Data
Creating XML Data