Parse xml with php simple xml - php

I have the following xml file:
<File>
<Function name="for">
<Function name="if">
<Function name="if">
</Function>
</Function>
<Function name="else">
</Function>
</Function>
</File>
I'm using php Simple xml parser to get all functions from this file and display them. What i want to get is something like this :
for{
if{
if{
}
}
else{
}
}
So to do that it is not sufficient to get all nodes that contain a function, i need to get the order as well.
I made a class called Function that have a list of functions:
class Function{
/**
* #var int
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Function")
*/
private listFunctions;
Now every function can have nested functions.
My parsing is as follows:
$xml = simplexml_load_file('file.xml') or die("Error: Cannot create object");
$functions = array();
foreach ($xml->->File->children() as $f) {
// Iterate over <Function> tags under <File> tag (level-1 functions)
$function = new Function();
$function->setName($f['name']);
if(isset($f->Function)) {
foreach($f->children() as $cf){
//Iterate over level-2 functions
$cfunction = new Function();
$cfunction->setName($cf['name']);
$f->addListFunctions($cfunction);
}
}
$functions[] = $function;
}
This example works perfectly if I have two levels of nested functions, it will display the following:
for{
if{
}
else{
}
}
But I don't know hwo many levels I have, so I need to do it in a generic way.
I think the solution is to use a recursive function, but I'm not used to it, and I don't know how to start, or even if it's the right path to follow.
Any idea ?

Related

PHP & XML Creation: Node in wrong parent

I am working currently on an OOP based PHP project in MVC style.
For my project i need to create/send/recieve/process XMLs.
Now i have a BIG problem with creating XML-Structures with DOMDocument.
Everytime i create a new XML-Node without an attributes or values, all nodes afterwards will be a child if this node!
In other words: I can not create an empty XML-Node without all nodes afterwards beeing a child of this empty node!!!
This problem bugs me for while now but I really need the way I am dealing right now with the XML creation.
I couldn't find any solutions but some similar problems.
This PHP tests my XmlHandler-Class, which creates the XML-Request:
Test.php:
<?php
include "Handler/XmlHandler.php";
$xmlHandler=new XmlHandler();
$xmlHandler->CreateNewXmlInstance();
$root = $xmlHandler->CreateRootNode('RootNode');
$l1 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel1", "Text1 - This node one has text");
$l2 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel2", "Text2 - Next node Level3 level is not gonna have text");
$l21 = $xmlHandler->AppendNodeWithChild($l2, "NodeLevel2_1", "Text2_1 - This node will be a child of Level2, everything fine");
$l3 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel3", "");
$l4 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel4", "Text4 - This node should be on same level like 3, 2 & 1, but instead it's a child of Level 3 (?!?!?!?!)");
echo "<p style='display:none;'>".$xmlHandler->SaveXml()."</p>";
?>
Here is the XML-Handler Class which i use to create the XML-Request-Structure (i just posted the neccessary parts of the class here)
XmlHandler.php:
<?php
class XmlHandler{
private $xml;
/**
*
* Constructor
*
*/
function __construct()
{
$this->xml=null;
}
//[...]
/*
* Custom XML-Creator Functions
*
*/
public function CreateNewXmlInstance(){
/*********************************************/
/** XML DOM example of building XML Request **/
/*********************************************/
$this->xml = new DOMDocument('1.0', 'UTF-8');
return $this->xml;
}
public function CreateRootNode($name){
$rootElement = $this->xml->appendChild( $this->xml->createElement($name) );
return $rootElement;
}
public function AppendNodeWithChild($node, $childName, $childText){
$result = $node->appendChild($this->xml->createElement($childName));
if(null != $childText && !empty($childText)){
$result->appendChild( $this->xml->createTextNode($childText) );
}
return $result;
}
public function SetNodeAttributes($node, $nameAndValues){
if(null != $nameAndValues && sizeof($nameAndValues) > 0){
foreach($nameAndValues as $name => $value){
$this->SetNodeAttribute($node, $name, $value);
}
}
}
public function SetNodeAttribute($node, $name, $value){
$node->setAttribute($name, $value);
}
public function SaveXml(){
return $this->xml->saveXML();
}
//[...]
}
?>
This is the Result:
<!-- ?xml version="1.0" encoding="UTF-8"? -->
<rootnode>
<nodelevel1>Text1 - This node one has text</nodelevel1>
<nodelevel2>Text2 - Next node Level3 level is not gonna have text
<nodelevel2_1>Text2_1 - This node will be a child of Level2, everything fine</nodelevel2_1>
</nodelevel2>
<nodelevel3>
<nodelevel4>Text4 - This node should be on same level like 3, 2 & 1, but instead it's a child of Level 3 (?!?!?!?!)</nodelevel4>
</nodelevel3>
</rootnode>
But in theory, there should be somethign like that:
<!-- ?xml version="1.0" encoding="UTF-8"? -->
<rootnode>
<nodelevel1>Text1 - This node one has text</nodelevel1>
<nodelevel2>Text2 - Next node Level3 level is not gonna have text
<nodelevel2_1>Text2_1 - This node will be a child of Level2, everything fine</nodelevel2_1>
</nodelevel2>
<nodelevel3/>
<nodelevel4>Text4 - This node should be on same level like 3, 2 & 1, but instead it's a child of Level 3 (?!?!?!?!)</nodelevel4>
</rootnode>
As you can see: Something went wrong when i had not set a value for the new created Node on Level3!:
$l3 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel3", "");
$l4 = $xmlHandler->AppendNodeWithChild($root, "NodeLevel4", "Text4 - This node should be on same level like 3, 2 & 1, but instead it's a child of Level 3 (?!?!?!?!)");
As long as i set attributes or put in value sin the new created node, everything is fine.
But i have some situations where also pure empty nodes have to be created!
My question is:
What am I doing wrong here?
Or does PHP do something wrong?
Maybe my browser does a bad preparation of the XML, but the outgoing XML request was build and send correctly and the mistake lies something else?
If so, how can I check the XML request though?
Edit Nr.2:
I changed my question/original post somehow.
The above example is a bit more easy to understand.
At least i hope so.
Wrap your helper functions in a class
class XMLHelper {
/*
* Custom XML-Creator Functions
*
*/
private $xml;
public function CreateNewXmlInstance(){
/*********************************************/
/** XML DOM example of building XML Request **/
/*********************************************/
$this->xml = new DOMDocument('1.0', 'UTF-8');
return $this->xml;
}
public function CreateRootNode($name){
$rootElement = $this->xml->appendChild( $this->xml->createElement($name) );
return $rootElement;
}
public function AppendNodeWithChild($node, $childName, $childText){
$result = $node->appendChild($this->xml->createElement($childName));
if(null != $childText && !empty($childText)){
$result->appendChild( $this->xml->createTextNode($childText) );
}
return $result;
}
public function SetNodeAttributes($node, $nameAndValues){
if(null != $nameAndValues && sizeof($nameAndValues) > 0){
foreach($nameAndValues as $name => $value){
$this->SetNodeAttribute($node, $name, $value);
}
}
}
public function SetNodeAttribute($node, $name, $value){
$node->setAttribute($name, $value);
}
public function SaveXml(){
return $this->xml->saveXML();
}
}
Wrap things in class because your code contain $this calls
Wrapping things in class make $this calls to correct variables the program needs.
Then new the class and initialize the nodes
$test = new XMLHelper();
$test->CreateNewXmlInstance();
$request = $test->CreateRootNode("request");
$node1 = $test->AppendNodeWithChild($request, "node1", null);
$node2 = $test->AppendNodeWithChild($node1, "node2", null);
$test->SetNodeAttributes($node2, array(
"client" => "This is a testing value"
));
echo $test->saveXml();
//Output:
// <?xml version="1.0" encoding="UTF-8"?>
// <request>
// <node1>
// <node2 client="This is a testing value"/>
// </node1>
// </request>
Please be noted that code need to executed in a correct sequence.
New the root node ( request )
Then append a node 1 to ( request )
Finally append node 2 to node 1
Configure the client value of node 2
Print out the whole root node
Then you've done the magic
There is nothing wrong with PHP neither with your web browser.
The error probably come from you haven't initialize the root node and not adding the created node correctly to the root node.
For XML validation in php, see this link.
DOMDocument has a function built-in for validation.
Edit 2016-08-10
Here are the revised code of your case
class SpecificXmlHandler extends XmlHandler{
/**
* Constructor
*
*/
private $errorCounter;
private $xmlUrl;
//Declare the root first
private $root;
function __construct()
{
parent::__construct();
$this->errorCounter=0;
$this->xmlUrl=Configuration::XML_REQUEST_URL;
}
//[...]
/**
* Action Functions
*/
public function GetStaticData($requestName, $requestFilterNamesAndValues){
$xml = $this->BuildStaticDataRequest($requestName, $requestFilterNamesAndValues);
echo "<p style='display:none'>" . $xml. "</p>"; //Request
$response = $this->ExecuteRequest($this->xmlUrl, $xml, null, false);
echo "<p style='display:none'>" . $response . "</p>"; //Resonse
//[...]
}
//[...]
/**
* Request Building Functions
*/
public function BuildStaticDataRequest($requestName, $requestFilterNamesAndValues){
$this->CreateNewXmlInstance();
//$root = $this->CreateRootNode('Request');
//Use the root node you have created in the constructor by using $this
$this->root = $this->CreateRootNode('Request');
//Generate Header (Source-Node)
$this->GenerateHeadData($this->root);
//[...]
return $this->SaveXml();
}
public function GenerateHeadData($root){
$clientID=Configuration::XML_CLIENT;
//Here the Node1 & Node2 creation
$node1 = $this->AppendNodeWithChild($root, "node1", null);
$node2 = $this->AppendNodeWithChild($node1, "node2", null);
$this->SetNodeAttributes( $node2 , array(
"Client" =>$clientID
));
//Change it to return the whole root
return $root;
}
[...]
}
Please create variables in the class and use $this to refer the private variables you have created.
Ok guys, somebody helped me with this problem.
Here is the conversation:
Him: The tags are wrong (since they've been changed to all-lowercase)
Me: Yes, it seems the XML gets messy after echoing it out into the browser instead of saving it into a file!
Him: And the output shouldn't even be formatted
Me: I did that myself for a better view
Him: This is the raw output of $xmlHandler->SaveXml() after enabling formatted output
<?xml version="1.0" encoding="UTF-8"?>
<RootNode>
<NodeLevel1>Text1 - This node one has text</NodeLevel1>
<NodeLevel2>Text2 - Next node Level3 level is not gonna have text<NodeLevel2_1>Text2_1 - This node will be a child of Level2, everything fine</NodeLevel2_1></NodeLevel2>
<NodeLevel3/>
<NodeLevel4>Text4 - This node should be on same level like 3, 2 & 1, but instead it's a child of Level 3 (?!?!?!?!)</NodeLevel4>
</RootNode>
Me: After printing it out into a file, i got the same result as you.
The conclusion:
Now I've wrote the saveXml-output into a file.
The Result is he same as Him told me.
So were was the mess?
I thoght it would be enough to print the XML out into the browser with echo and check it through the HTML-sourcecode via FireFox.
As you can see in my first post, i used echo...
echo "<p style='display:none;'>".$xmlHandler->SaveXml()."</p>";
to somehow print out the result of my XML-Creation (because i didn't know how else).
Therefore the XML-Creation was a full success from start up and the error i get from the response-Server lies somewhere else (from now on i can not rely on your help anymore)!

How to change root of a node with DOMDocument methods?

How to only change root's tag name of a DOM node?
In the DOM-Document model we can not change the property documentElement of a DOMElement object, so, we need "rebuild" the node... But how to "rebuild" with childNodes property?
NOTE: I can do this by converting to string with saveXML and cuting root by regular expressions... But it is a workaround, not a DOM-solution.
Tried but not works, PHP examples
PHP example (not works, but WHY?):
Try-1
// DOMElement::documentElement can not be changed, so...
function DomElement_renameRoot1($ele,$ROOTAG='newRoot') {
if (gettype($ele)=='object' && $ele->nodeType==XML_ELEMENT_NODE) {
$doc = new DOMDocument();
$eaux = $doc->createElement($ROOTAG); // DOMElement
foreach ($ele->childNodes as $node)
if ($node->nodeType == 1) // DOMElement
$eaux->appendChild($node); // error!
elseif ($node->nodeType == 3) // DOMText
$eaux->appendChild($node); // error!
return $eaux;
} else
die("ERROR: invalid DOM object as input");
}
The appendChild($node) cause an error:
Fatal error: Uncaught exception 'DOMException'
with message 'Wrong Document Error'
Try-2
From #can suggestion (only pointing link) and my interpretation of the poor dom-domdocument-renamenode manual.
function DomElement_renameRoot2($ele,$ROOTAG='newRoot') {
$ele->ownerDocument->renameNode($ele,null,"h1");
return $ele;
}
The renameNode() method caused an error,
Warning: DOMDocument::renameNode(): Not yet implemented
Try-3
From PHP manual, comment 1.
function renameNode(DOMElement $node, $newName)
{
$newNode = $node->ownerDocument->createElement($newName);
foreach ($node->attributes as $attribute)
$newNode->setAttribute($attribute->nodeName, $attribute->nodeValue);
while ($node->firstChild)
$newNode->appendChild($node->firstChild); // changes firstChild to next!?
$node->ownerDocument->replaceChild($newNode, $node); // changes $node?
// not need return $newNode;
}
The replaceChild() method caused an error,
Fatal error: Uncaught exception 'DOMException' with message 'Not Found Error'
As this has not been really answered yet, the error you get about not found is because of a little error in the renameNode() function you've copied.
In a somewhat related question about renaming different elements in the DOM I've seen this problem as well and used an adoption of that function in my answer that does not have this error:
/**
* Renames a node in a DOM Document.
*
* #param DOMElement $node
* #param string $name
*
* #return DOMNode
*/
function dom_rename_element(DOMElement $node, $name) {
$renamed = $node->ownerDocument->createElement($name);
foreach ($node->attributes as $attribute) {
$renamed->setAttribute($attribute->nodeName, $attribute->nodeValue);
}
while ($node->firstChild) {
$renamed->appendChild($node->firstChild);
}
return $node->parentNode->replaceChild($renamed, $node);
}
You might have spotted it in the last line of the function body: This is using ->parentNode instead of ->ownerDocument. As $node was not a child of the document, you did get the error. And it also was wrong to assume that it should be. Instead use the parent element to search for the child in there to replace it ;)
This has not been outlined in the PHP manual usernotes so far, however, if you did follow the link to the blog-post that originally suggested the renameNode() function you could find a comment below it offering this solution as well.
Anyway, my variant here uses a slightly different variable naming and is more distinct about the types. Like the example in the PHP manual it misses the variant that deals with namespace nodes. I'm not yet booked what would be best, e.g. creating an additional function dealing with it, taking over namespace from the node to rename or changing the namespace explicitly in a different function.
First, you need to understand that the DOMDocument is only the hierarchical root of the document-tree. It's name is always #document. You want to rename the root-element, which is the $document->documentElement.
If you want to copy nodes form a document to another document, you'll need to use the importNode() function: $document->importNode($nodeInAnotherDocument)
Edit:
renameNode() is not implemented yet, so you should make another root, and simply replace it with the old one. If you use DOMDocument->createElement() you don't need to use importNode() on it later.
$oldRoot = $doc->documentElement;
$newRoot = $doc->createElement('new-root');
foreach ($oldRoot->attributes as $attr) {
$newRoot->setAttribute($attr->nodeName, $attr->nodeValue);
}
while ($oldRoot->firstChild) {
$newRoot->appendChild($oldRoot->firstChild);
}
$doc->replaceChild($newRoot, $oldRoot);
This is an variation of my "Try-3" (see question), and works fine!
function xml_renameNode(DOMElement $node, $newName, $cpAttr=true) {
$newNode = $node->ownerDocument->createElement($newName);
if ($cpAttr && is_array($cpAttr)) {
foreach ($cpAttr as $k=>$v)
$newNode->setAttribute($k, $v);
} elseif ($cpAttr)
foreach ($node->attributes as $attribute)
$newNode->setAttribute($attribute->nodeName, $attribute->nodeValue);
while ($node->firstChild)
$newNode->appendChild($node->firstChild);
return $newNode;
}    
Of course, if you show how to use DOMDocument::renameNode (without errors!), the bounty goes for you!
ISTM in your approach you attempt to import nodes from another DOMDocument, so you need to use the importNode() method:
$d = new DOMDocument();
/* Make a `foo` element the root element of $d */
$root = $d->createElement("foo");
$d->appendChild($root);
/* Append a `bar` element as the child element of the root of $d */
$child = $d->createElement("bar");
$root->appendChild($child);
/* New document */
$d2 = new DOMDocument();
/* Make a `baz` element the root element of $d2 */
$root2 = $d2->createElement("baz");
$d2->appendChild($root2);
/*
* Import a clone of $child (from $d) into $d2,
* with its child nodes imported recursively
*/
$child2 = $d2->importNode($child, true);
/* Add the clone as the child node of the root of $d2 */
$root2->appendChild($child2);
However, it is far easier to append the child nodes to a new parent element (thereby moving them), and replace the old root with that parent element:
$d = new DOMDocument();
/* Make a `foo` element the root element of $d */
$root = $d->createElement("foo");
$d->appendChild($root);
/* Append a `bar` element as the child element of the root of $d */
$child = $d->createElement("bar");
$root->appendChild($child);
/* <?xml version="1.0"?>
<foo><bar/></foo> */
echo $d->saveXML();
$root2 = $d->createElement("baz");
/* Make the `bar` element the child element of `baz` */
$root2->appendChild($child);
/* Replace `foo` with `baz` */
$d->replaceChild($root2, $root);
/* <?xml version="1.0"?>
<baz><bar/></baz> */
echo $d->saveXML();
I hope I am not missing anything but I happened to have the similar problem and was able to solve it by using use DomDocument::replaceChild(...).
/* #var $doc DOMDocument */
$doc = DOMImplementation::createDocument(NULL, 'oldRoot');
/* #var $newRoot DomElement */
$newRoot = $doc->createElement('newRoot');
/* all the code to create the elements under $newRoot */
$doc->replaceChild($newRoot, $doc->documentElement);
$doc->documentElement->isSameNode($newRoot) === true;
What threw me off initially was that $doc->documentElement was readonly, but the above worked and seems to be much simpler solution IF the $newRoot was created with the same DomDocument, otherwise you'll need do the importNode solution as described above. From your question is appears that $newRoot could be created from the same $doc.
Let us know if this worked out for you. Cheers.
EDIT: Noticed in version 20031129 that the DomDocument::$formatOutput, if set, does not format $newRoot output when you finally call $doc->saveXML()

How to serialize/save a DOMElement in $_SESSION?

I'm pretty new to PHP, DOM, and the PHP DOM implementation. What I'm trying to do is save the root element of the DOMDocument in a $_SESSION variable so I can access it and modify it on subsequent page loads.
But I get an error in PHP when using $_SESSION to save state of DOMElement:
Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement
I have read that a PHP DOMDocument object cannot be saved to $_SESSION natively. However it can be saved by saving the serialization of the DOMDocument (e.g. $_SESSION['dom'] = $dom->saveXML()).
I don't know if the same holds true for saving a DOMElement to a $_SESSION variable as well, but that's what I was trying. My reason for wanting to do this is to use an extended class of DOMElement with one additional property. I was hoping that by saving the root DOMElement in $_SESSION that I could later retrieve the element and modify this additional property and perform a test like, if (additionalProperty === false) { do something; }. I've also read that by saving a DOMDocument, and later retrieving it, all elements are returned as objects from native DOM classes. That is to say, even if I used an extended class to create elements, the property that I subsequently need will not be accessible, because the variable holding reference to the extended-class object has gone out of scope--which is why I'm trying this other thing. I tried using the extended class (not included below) first, but got errors...so I reverted to using a DOMElement object to see if that was the problem, but I'm still getting the same errors. Here's the code:
<?php
session_start();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$_SESSION[$rootTag] = new DOMElement($rootTag);
}
$root = $doc->appendChild($_SESSION[$rootTag]);
//$root = $doc->appendChild($doc->importNode($_SESSION[$rootTag], true));
$child = new DOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) echo '<br/>'.$ch->tagName.' '.++$ct;
$_SESSION[$rootTag] = $doc->documentElement;
?>
This code gives the following errors (depending on whether I use appendChild directly or the commented line of code using importNode):
Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 11
Warning: DOMDocument::importNode() [domdocument.importnode]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 12
I have several questions. First, what is causing this error and how do I fix it? Also, if what I'm trying to do isn't possible, then how can I accomplish my general objective of saving the 'state' of a DOM tree while using a custom property for each element? Note that the additional property is only used in the program and is not an attribute to be saved in the XML file. Also, I can't just save the DOM back to file each time, because the DOMDocument, after a modification, may not be valid according to a schema I'm using until later when additional modificaitons/additions have been performed to the DOMDocument. That's why I need to save a temporarily invalid DOMDocument. Thanks for any advice!
EDITED:
After trying hakre's solution, the code worked. Then I moved on to trying to use an extended class of DOMElement, and, as I suspected, it did not work. Here's the new code:
<?php
session_start();
//$_SESSION = array();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$root = new FreezableDOMElement($rootTag);
$doc->appendChild($root);
} else {
$doc->loadXML($_SESSION[$rootTag]);
$root = $doc->documentElement;
}
$child = new FreezableDOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) {
$frozen = $ch->frozen ? 'is frozen' : 'is not frozen';
echo '<br/>'.$ch->tagName.' '.++$ct.': '.$frozen;
//echo '<br/>'.$ch->tagName.' '.++$ct;
}
$_SESSION[$rootTag] = $doc->saveXML();
/**********************************************************************************
* FreezableDOMElement class
*********************************************************************************/
class FreezableDOMElement extends DOMElement {
public $frozen; // boolean value
public function __construct($name) {
parent::__construct($name);
$this->frozen = false;
}
}
?>
It gives me the error Undefined property: DOMElement::$frozen. Like I mentioned in my original post, after saveXML and loadXML, an element originally instantiated with FreezableDOMElement is returning type DOMElement which is why the frozen property is not recognized. Is there any way around this?
You can not store a DOMElement object inside $_SESSION. It will work at first, but with the next request, it will be unset because it can not be serialized.
That's the same like for DOMDocument as you write about in your question.
Store it as XML instead or encapsulate the serialization mechanism.
You are basically facing three problems here:
Serialize the DOMDocument (you do this to)
Serialize the FreezableDOMElement (you do this to)
Keep the private member FreezableDOMElement::$frozen with the document.
As written, serialization is not available out of the box. Additionally, DOMDocument does not persist your FreezableDOMElement even w/o serialization. The following example demonstrates that the instance is not automatically kept, the default value FALSE is returned (Demo):
class FreezableDOMElement extends DOMElement
{
private $frozen = FALSE;
public function getFrozen()
{
return $this->frozen;
}
public function setFrozen($frozen)
{
$this->frozen = (bool)$frozen;
}
}
class FreezableDOMDocument extends DOMDocument
{
public function __construct()
{
parent::__construct();
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
# own objects do not persist
$doc->documentElement->setFrozen(TRUE);
printf("Element is frozen (should): %d\n", $doc->documentElement->getFrozen()); # it is not (0)
As PHP does not so far support setUserData (DOM Level 3), one way could be to store the additional information inside a namespaced attribute with the element. This can also be serialized by creating the XML string when serializing the object and loading it when unserializing (see Serializable). This then solves all three problems (Demo):
class FreezableDOMElement extends DOMElement
{
public function getFrozen()
{
return $this->getFrozenAttribute()->nodeValue === 'YES';
}
public function setFrozen($frozen)
{
$this->getFrozenAttribute()->nodeValue = $frozen ? 'YES' : 'NO';
}
private function getFrozenAttribute()
{
return $this->getSerializedAttribute('frozen');
}
protected function getSerializedAttribute($localName)
{
$namespaceURI = FreezableDOMDocument::NS_URI;
$prefix = FreezableDOMDocument::NS_PREFIX;
if ($this->hasAttributeNS($namespaceURI, $localName)) {
$attrib = $this->getAttributeNodeNS($namespaceURI, $localName);
} else {
$this->ownerDocument->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . $prefix, $namespaceURI);
$attrib = $this->ownerDocument->createAttributeNS($namespaceURI, $prefix . ':' . $localName);
$attrib = $this->appendChild($attrib);
}
return $attrib;
}
}
class FreezableDOMDocument extends DOMDocument implements Serializable
{
const NS_URI = '/frozen.org/freeze/2';
const NS_PREFIX = 'freeze';
public function __construct()
{
parent::__construct();
$this->registerNodeClasses();
}
private function registerNodeClasses()
{
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
/**
* #return DOMNodeList
*/
private function getNodes()
{
$xp = new DOMXPath($this);
return $xp->query('//*');
}
public function serialize()
{
return parent::saveXML();
}
public function unserialize($serialized)
{
parent::__construct();
$this->registerNodeClasses();
$this->loadXML($serialized);
}
public function saveBareXML()
{
$doc = new DOMDocument();
$doc->loadXML(parent::saveXML());
$xp = new DOMXPath($doc);
foreach ($xp->query('//#*[namespace-uri()=\'' . self::NS_URI . '\']') as $attr) {
/* #var $attr DOMAttr */
$attr->parentNode->removeAttributeNode($attr);
}
$doc->documentElement->removeAttributeNS(self::NS_URI, self::NS_PREFIX);
return $doc->saveXML();
}
public function saveXMLDirect()
{
return parent::saveXML();
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
$doc->documentElement->setFrozen(TRUE);
$child = $doc->getElementsByTagName('child')->item(0);
$child->setFrozen(TRUE);
echo "Plain XML:\n", $doc->saveXML(), "\n";
echo "Bare XML:\n", $doc->saveBareXML(), "\n";
$serialized = serialize($doc);
echo "Serialized:\n", $serialized, "\n";
$newDoc = unserialize($serialized);
printf("Document Element is frozen (should be): %s\n", $newDoc->documentElement->getFrozen() ? 'YES' : 'NO');
printf("Child Element is frozen (should be): %s\n", $newDoc->getElementsByTagName('child')->item(0)->getFrozen() ? 'YES' : 'NO');
It's not really feature complete but a working demo. It's possible to obtain the full XML without the additional "freeze" data.

PHP Class Function List in Comments

Whenever I write a class in PHP, I like to put a list of public functions at the top of the class like this:
// add_project($apikey, $post, $email)
// create_user($apikey, $email, $firstname, $lastname, $rolename, $pid)
// user_join_project($apikey, $email, $pid)
I was wondering if there were any tools that can automate this process. Like perhaps I could load the class file and it could generate a list of the function names, variables and such?
Thanks!
Try phpDocumentor. You use DocBlock syntax for comments (similar to Javadoc and in other languages) then pass your PHP source files through the phpDocumentor parser, and it generates API documentation for you.
A rough example:
/**
* Adds a project.
*
* #param string $apikey The API key.
* #param object $post The post.
* #param string $email A supplied email address.
* #return void
*/
function add_project($apikey, $post, $email) {
...
}
There's a not great deal of value in this approach.
First off, if you're not already doing so you should mark each of your methods (and indeed member variables) with the appropriate visibility (public/protected/private). You could then use a solution such as phpDoc, which to properly document each method argument in addition to providing an overall purpose for each method/class, etc.
You can then automatically generate documentation for your project in HTML format (amongst others).
There are various tools for this. They are called PHP documentor's. I use an IDE called PHPStorm that has a documentor integrated.
Here is some more information on PHPDoc.
This can be done pretty easily with Parsekit.
Using this ("tmp.php") as sample data:
<?php
class Fruit
{
public function apple($cored) {}
public function orange($peeled) {}
public function grape($colour, $seedless) {}
}
Here's a simple Parsekit example to dump a class's functions:
<?php
$parsed = parsekit_compile_file($argv[1]);
foreach ($parsed['class_table'] as $class => $classdat) {
foreach ($classdat['function_table'] as $func => $funcdat) {
echo "{$class}::{$func}(";
$first = true;
foreach ($funcdat['arg_info'] as $arg => $argdat) {
if (!$first) {
echo ', ';
}
echo "\${$argdat['name']}";
$first = false;
}
echo ")\n";
}
}
…and here it is in use:
$ php parse.php tmp.php
Fruit::grape($colour, $seedless)
Fruit::orange($peeled)
Fruit::apple($cored)

How do I call this function in CodeIgniter controller?

I've got this code, but I'm not sure I make it work:
/**
* Function: youtube data grabber
*
* #description :
* #param $ : video code, url type (embed/url)
* #return : data array
* #author : Mamun.
* #last -modified-by: Mamun.
*/
if (! function_exists('youtube_data_grabber'))
{
function youtube_data_grabber($video_code, $link_type = "embed")
{
if ($video_code != '')
{
if ($link_type == "embed")
{
$splited_data = explode("=",$video_code);
$video_unique_code = substr(strrchr($splited_data[4],"/"),1,-strlen(strrchr($splited_data[4],"&")));
}
else if ($link_type == "url")
{
$splited_data = explode("=",$video_code);
$video_unique_code = substr($splited_data[1],0,-strlen(strrchr($splited_data[1],"&")));
}
else
{
return;
}
// set feed URL
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/'.$video_unique_code;
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
return $sxml;
}
}
} // End Youtube Function
I'm not sure how to activate it is what I'm trying to say. I placed it in the controller and it's within a function for one of my pages. I don't have any syntax errors. I just don't know how to wake it up and make it work. I thought I could just put youtube_data_grabber('http://www.youtube.com/watch?v=LAcrFym10ZI', 'url'); but that didn't work.
I got the code from this blog, and I have the zend functionality working. I tested it earlier and had no errors. I'm just having trouble with this youtube part.
Any ideas?
That code should go in a helper or plugin not in the controller. The first part of the code on that page should be in your controller. The one you pasted is just an alternate version.
Save your code to application/helpers/youtube_helper.php, then in your controller go ahead and call $this->load->helper('youtube').
Only then will your youtube_data_grabber() function be available.

Categories