[PHP] dom xml implementatie: niet simpel!

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Ik doe een poging een XML Document Object Model in PHP te maken. Dit lukt over 't algemeen aardig, maar ik zit nu al een week te ploeteren op de insertBefore-method en ik kom er niet uit. :'(

Ik heb hulp nodig! De versie van m'n class, waar ik nu mee bezig ben, staat online op mijn site: http://www.webtweakers.com/phpdomxml/beta/functest.php

Dat is een link naar de functionality test case. In die pagina staat ook een link naar een ZIP-file, waar het hele pakketje te downloaden is.

Kan iemand me een duwtje in de goede richting geven?

Hier vast de insertBefore-method, zoals ik 'm nu heb (die dus niet werkt):

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
        function insertBefore(&$child, $refChild = null) {

            // If no refChild was given, append child
            if (is_null($refChild)) $this->appendChild($child);

            // Set child's parentNode
            $child->parentNode =& $this;

            // Get child that is now before refChild
            $pChild =& $refChild->previousSibling;

            // Insert child between pChild and refChild
            $child->previousSibling =& $pChild;
            $child->nextSibling =& $refChild;

            // Set next child's previousSibling
            $refChild->previousSibling =& $child;

            if (is_null($pChild)) {

                // Since there is no previous child, our child is firstChild
                $this->firstChild =& $child;

            } else {

                // Set previous child's nextSibling
                $pChild->nextSibling =& $child;

            }

            // Now rebuild childNodes collection
            $i = 0;
            $item = $this->firstChild;
            while ($item) {
                $this->childNodes[$i++] = $item;
                $item = isset($item->nextSibling)?$item->nextSibling:null;
            }

            // Finaly set firstChild and lastChild
            $this->firstChild = $this->childNodes[0];
            $this->lastChild = $this->childNodes[count($this->childNodes)-1];

            // Return the inserted child - as per spec
            return $child;
            
        } // End insertBefore

[ Voor 55% gewijzigd door Explore op 18-10-2003 17:34 ]

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

Verwijderd

Je moet bij het zetten van oreviousSibling van refChild kijke of refChild niet null is. In bijvoorbeeld Java zou je dan een NullPointerException krijgen.

childNodes is trouwens geen array, maar een NodeList.

Verder check je helemaal niets van de node hiërarchie, wat toch wel vrij essentieel is. Ik schat dat 2/3 van mijn Java implementatie neerkwam op controleren en het gooien van exceptions.

Maar sorry, met 'het werkt niet' kan ik niets.

Hoe kom je er trouwens bij dat een Node een Text class extend? Dat is dus andersom!
Als je een beetje een goede implementatie wilt maken, begin dan eerst eens met het maken van een NodeList en een NamedNodeMap class, daarmee maak je heel wat operaties stukken makkelijker.

Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Verwijderd schreef op 18 October 2003 @ 18:44:
Je moet bij het zetten van oreviousSibling van refChild kijke of refChild niet null is. In bijvoorbeeld Java zou je dan een NullPointerException krijgen.
Dat hoeft niet, want als refChild null is, dan wordt appendChild aangeroepen... En terwijl ik dit schrijf zie ik dat ik een return vergeten ben. |:( De rest van de functie moet dan niet meer doorlopen worden. Scheelt gelijk een hoop error-meldingen, zie ik. 100% werken doet 't dan nog niet, overigens.

Dit dus:

PHP:
1
2
3
4
5
// If no refChild was given, append child
if (is_null($refChild)){
    $this->appendChild($child);
    return $child;
}


Dit fixt veel, maar nog niet alles. De previousSibling's lopen nog ergens de mist in...
childNodes is trouwens geen array, maar een NodeList.
Weet ik. Ik volg de standaard niet 100%, aangezien het hier om PHP gaat. Als ik van childNodes en object maak, dan kan ik de childNodes niet meer als array aanspreken (wat juist handig is), bijvoorbeeld:

PHP:
1
$this->childNodes[$i++] = $item;
Verder check je helemaal niets van de node hiërarchie, wat toch wel vrij essentieel is. Ik schat dat 2/3 van mijn Java implementatie neerkwam op controleren en het gooien van exceptions.
Tsja. Zoals ik al schreef ben ik nog niet klaar met het maken van de insertBefore-method. Kijk bijvoorbeeld naar de appendChild-method. Die zit nu stabiel in elkaar en daar zitten ook wat meer checks in.
Maar sorry, met 'het werkt niet' kan ik niets.
insertBefore heeft maar 1 doel en dat doet-ie niet. Need I say more? :Y)
Hoe kom je er trouwens bij dat een Node een Text class extend? Dat is dus andersom!
Lijkt me vreemd. Een Text heeft niet de zelfde properties en methods als Node. Wat heeft een Text-node nou aan attributes bijvoorbeeld?
Als je een beetje een goede implementatie wilt maken, begin dan eerst eens met het maken van een NodeList en een NamedNodeMap class, daarmee maak je heel wat operaties stukken makkelijker.
De implementatie waar ik mee bezig ben volgt de implementatie van de XML DOM zoals deze toegepast wordt in Javascript en Actionscript. Ik heb de opbouw van classes dan ook geheel zelf verzonnen, mede omdat niet alles volgens de 'voorschriften' van het w3c te implementeren is in PHP (of ik moet me grondig vergissen, wat niet onmogelijk is).

Mijn implementatie, zoals hij er nu voor staat, werkt perfect en precies volgens de standaard wat 'm erg makkelijk in gebruik maakt.

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09 23:18

djc

Maar kan je nauwkeuriger aangeven waar het fout gaat? Zijn er momenten in je functie dattie nog bezig is met doen wat ie moet doen? Waar gaat het mis? Waarom staan er geen 16 var_dump()s in de functie om te kijken wat er gebeurt?

Overigens lijkt het me wel interessant om jouw implementatie te benchmarken tegenover de DOMXML extensie die als module bij PHP geleverd wordt.

Rustacean


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Manuzhai schreef op 18 oktober 2003 @ 23:50:
Maar kan je nauwkeuriger aangeven waar het fout gaat? Zijn er momenten in je functie dattie nog bezig is met doen wat ie moet doen? Waar gaat het mis? Waarom staan er geen 16 var_dump()s in de functie om te kijken wat er gebeurt?
Alle vardumps heb ik er uitgehaald voordat ik de code hier poste, om het zaakje een beetje overzichtelijk te houden.

In de testcode heb ik een array van 4 elementen die ik mbv. insertBefore in een XML DOM prop. Als je deze dus weer uitleest mbv. nextSibling, dan verwacht je de 4 elementen achterstevoren terug te zien komen. Met previousSibling verwacht je ze in de originele volgorde en mbv. childNodes weer achterstevoren. Snap je?

De output van m'n testscript is nu:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Walk tree with nextSibling
4: still more text here
3: even more text here
2: some more text here
1: some text here

Walk tree with previousSibling
1: some text here
2: some more text here

Walk tree with childNodes
1: some text here
3: even more text here
2: some more text here
1: some text here


Dus de output bij nextSibling klopt: die links worden blijkbaar goed gelegd. De output bij previousSibling en childNodes klopt echter niet. Waar dit nu precies fout gaat in de insertBefore-method is mij niet duidelijk.
Overigens lijkt het me wel interessant om jouw implementatie te benchmarken tegenover de DOMXML extensie die als module bij PHP geleverd wordt.
Tsja, ik verwacht dat de implementatie van PHP, die gebruik maakt van libxml, sneller is, aangezien die in C geschreven is.

Het was mij in eerste instantie te doen om bruikbaarheid en compatibiliteit met de w3c en benadering van de DOM op een gelijke wijze zoals in Javascript en Actionscript. Bij de DOMXML extensie in PHP zijn bepaalde properties methods, wat mijn inziens niet de w3c volgt.

Ik zou overigens cheatah's raad op willen volgen om bv. NodeList en NamedNodeMap objects te definieren, maar op deze pagina zie je bijvoorbeeld dit:
(Object NodeList)
Note: This object can also be dereferenced using square bracket notation (e.g. obj[1]). Dereferencing with an integer index is equivalent to invoking the item method with that index.
Hoe doe je dat in PHP? Volgens mij gewoon niet.

Ik volg echter wel de definities zoals ze op die pagina staan voor wat betreft docType, createElement, createTextNode, nodeName, nodeValue, nodeType, parentNode, childNodes, firstChild, lastChild, previousSibling, nextSibling, attributes, appendChild, hasChildNodes en zometeen dus ook insertBefore, replaceChild en removeChild. En wellicht nog wat andere properties en methods.

Btw: cheatah, overigens kan je hier zien dat het Text object niet het Node object extends, maar het CharacterData object. Probleem is met de expat-parser, die ik gebruikt om het XML document te parsen, vrij slordig om gaat met CData-elementen: deze worden alleen herkend als ze ge-isoleerd staan, maar zodra ze embedded zijn in een andere tag (zoals bv. een stuk code in een script-tag), dan worden ze gezien als text in een Text-node.

[ Voor 29% gewijzigd door Explore op 19-10-2003 14:09 ]

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09 23:18

djc

Hier is een herschreven versie. Ongetest, misschien heb je er wat aan.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function insertBefore(&$child, $refChild = null) {

    if (is_null($refChild)){

        $this->appendChild($child);
        return $child;

    }

    $child->parentNode =& $this;

    if (is_null($refChild->previousSibling)) {
    
        $child->previousSibling =& null;
        $child->nextSibling =& $refChild;

        $refChild->previousSibling =& $child;
        $this->firstChild =& $child;
        
    } else {
        
        $child->previousSibling =& $refChild->previousSibling;
        $child->nextSibling =& $refChild;
        
        $pChild =& $refChild->previousSibling;
        $refChild->previousSibling =& $child;
        $pChild->nextSibling =& $child;         
        
    }
    
    $next = null;
    $size = sizeof($this->childNodes);
    for ($i = 0; $i < $size; $i++) {
        
        if ($this->childNodes[$i] == $refChild) {
            
            $this->childNodes[$i] =& $child;
            $next =& $refChild;
            
        } elseif (isset($next)) {
            
            $temp = $this->childNodes[$i];
            $this->childNodes[$i] =& $next;
            $next = $temp;
            
        }
        
    }
    
    $this->childNodes[] =& $next;
    $this->firstChild = $this->childNodes[0];
    $this->lastChild = $this->childNodes[count($this->childNodes)-1];

    return $child;
    
}

Rustacean


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Zonder al te diep je code bekeken te hebben, heb ik 'm in m'n class gezet en m'n functionality test eroverheen gehaald. Ik krijg een foutmelding:
Parse error: parse error, unexpected ';', expecting T_PAAMAYIM_NEKUDOTAYIM or '(' in ...
Mooie melding. :) Zit in regel 14 van bovenstaand stukje code. De reden ontgaat me even, overigens. Verder verwacht ik een probleem met de vergelijking van objecten zoals in regel 35. Ik heb mijn versie van deze method juist geschreven zonder object-vergelijkingen, omdat die gauw de mist inlopen met al die references. Echt een killer!

Maargoed, 'k zal eens kijken wat er aan de hand is met die wazige foutmelding...

Bedankt voor de input!

Edit:

Overigens zie ik net dat als ik bij het zetten van de firstChild en lastChild ook refs gebruik, dat dan de childNodes lijst ook weer klopt. Dit bedoel ik, in vergelijking met mijn versie in de openingspost:

PHP:
1
2
3
// Finaly set firstChild and lastChild
$this->firstChild =& $this->childNodes[0];
$this->lastChild =& $this->childNodes[count($this->childNodes)-1];


De output van childNodes in de functionality test is dan:

code:
1
2
3
4
5
Walk tree with childNodes
4: still more text here
3: even more text here
2: some more text here
1: some text here


Nu kloppen alleen de previousSibling's nog niet. En ik denk dat je een aardig idee hebt gegeven in je code. Ik ga het bekijken...

[ Voor 36% gewijzigd door Explore op 19-10-2003 22:11 ]

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09 23:18

djc

Explore schreef op 19 October 2003 @ 22:05:
Mooie melding. :) Zit in regel 14 van bovenstaand stukje code. De reden ontgaat me even, overigens. Verder verwacht ik een probleem met de vergelijking van objecten zoals in regel 35. Ik heb mijn versie van deze method juist geschreven zonder object-vergelijkingen, omdat die gauw de mist inlopen met al die references. Echt een killer!
Die fout betekent dubbele dubbele punt in het Hebreeuws als ik het me goed herinner. De fout is waarschijnlijk dat ik niet =& null mag doen. :)

Rustacean


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Oja, good point. doh!

Nu krijg ik inderdaad een foutmelding bij de object-vergelijking, zoals verwacht:
Fatal error: Nesting level too deep - recursive dependency? in J:\www\projects\phpdomxml\lib.xml.inc.php on line 266
Dat is dus deze regel:

PHP:
1
if ($this->childNodes[$i] == $refChild) {


Komt vanwege al die refs.

Ik zal eens kijken of ik er omheen kan werken.

Jammer dat array_splice nutteloos is als je de index niet weet... :|

[ Voor 9% gewijzigd door Explore op 20-10-2003 21:08 ]

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Ik heb inmiddels een berg 'var_dumps' toegevoegd. Hier het resultaat:
http://www.webtweakers.com/phpdomxml/beta/functest.php

Er komt erg veel informatie voorbij voor een enkele insertBefore-actie, maar in de debug-output kan je lezen dat tijdens het zetten van alle referenties en het herschrijven van de childNodes-list alles nog in orde is. Ook kloppen firstChild en lastChild.

Aan het einde van elke insertBefore-actie, wordt nog eens van achter naar voren door de lijst gelopen en dan blijkt ineens de previousSibling van 't 2e child verdwenen te zijn: deze is null en dus stopt de loop.

Ik zit me hier nu al een paar uur blind op te staren en vroeg me af of iemand met een verse blik 't licht misschien ziet.

Op de test pagina (^^^) staat nog steeds een link naar een zip-file met alle code.

[ specs ] [ Tweaker gallery ]

Pagina: 1