summaryrefslogtreecommitdiff
path: root/lib/phptal/PHPTAL/Dom/PHPTALDocumentBuilder.php
blob: a3157beb1794049e6a329818434d4a5891d4eae9 (plain)
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?php
/**
 * PHPTAL templating engine
 *
 * PHP Version 5
 *
 * @category HTML
 * @package  PHPTAL
 * @author   Laurent Bedubourg <lbedubourg@motion-twin.com>
 * @author   Kornel Lesiński <kornel@aardvarkmedia.co.uk>
 * @license  http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
 * @version  SVN: $Id$
 * @link     http://phptal.org/
 */


/**
 * DOM Builder
 *
 * @package PHPTAL
 * @subpackage Dom
 */
class PHPTAL_Dom_PHPTALDocumentBuilder extends PHPTAL_Dom_DocumentBuilder
{
    private $_xmlns;   /* PHPTAL_Dom_XmlnsState */
    private $encoding;

    public function __construct()
    {
        $this->_xmlns = new PHPTAL_Dom_XmlnsState(array(), '');
    }

    public function getResult()
    {
        return $this->documentElement;
    }

    protected function getXmlnsState()
    {
        return $this->_xmlns;
    }

    // ~~~~~ XmlParser implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public function onDocumentStart()
    {
        $this->documentElement = new PHPTAL_Dom_Element('documentElement', 'http://xml.zope.org/namespaces/tal', array(), $this->getXmlnsState());
        $this->documentElement->setSource($this->file, $this->line);
        $this->_current = $this->documentElement;
    }

    public function onDocumentEnd()
    {
        if (count($this->_stack) > 0) {
            $left='</'.$this->_current->getQualifiedName().'>';
            for ($i = count($this->_stack)-1; $i>0; $i--) $left .= '</'.$this->_stack[$i]->getQualifiedName().'>';
            throw new PHPTAL_ParserException("Not all elements were closed before end of the document. Missing: ".$left,
                        $this->file, $this->line);
        }
    }

    public function onDocType($doctype)
    {
        $this->pushNode(new PHPTAL_Dom_DocumentType($doctype, $this->encoding));
    }

    public function onXmlDecl($decl)
    {
        if (!$this->encoding) {
            throw new PHPTAL_Exception("Encoding not set");
        }
        $this->pushNode(new PHPTAL_Dom_XmlDeclaration($decl, $this->encoding));
    }

    public function onComment($data)
    {
        $this->pushNode(new PHPTAL_Dom_Comment($data, $this->encoding));
    }

    public function onCDATASection($data)
    {
        $this->pushNode(new PHPTAL_Dom_CDATASection($data, $this->encoding));
    }

    public function onProcessingInstruction($data)
    {
        $this->pushNode(new PHPTAL_Dom_ProcessingInstruction($data, $this->encoding));
    }

    public function onElementStart($element_qname, array $attributes)
    {
        $this->_xmlns = $this->_xmlns->newElement($attributes);

        if (preg_match('/^([^:]+):/', $element_qname, $m)) {
            $prefix = $m[1];
            $namespace_uri = $this->_xmlns->prefixToNamespaceURI($prefix);
            if (false === $namespace_uri) {
                throw new PHPTAL_ParserException("There is no namespace declared for prefix of element < $element_qname >. You must have xmlns:$prefix declaration in the same document.",
                            $this->file, $this->line);
            }
        } else {
            $namespace_uri = $this->_xmlns->getCurrentDefaultNamespaceURI();
        }

        $attrnodes = array();
        foreach ($attributes as $qname=>$value) {

            if (preg_match('/^([^:]+):(.+)$/', $qname, $m)) {
                list(,$prefix, $local_name) = $m;
                $attr_namespace_uri = $this->_xmlns->prefixToNamespaceURI($prefix);

            if (false === $attr_namespace_uri) {
                    throw new PHPTAL_ParserException("There is no namespace declared for prefix of attribute $qname of element < $element_qname >. You must have xmlns:$prefix declaration in the same document.",
                            $this->file, $this->line);
            }
            } else {
                $local_name = $qname;
                $attr_namespace_uri = ''; // default NS. Attributes don't inherit namespace per XMLNS spec
            }

            if ($this->_xmlns->isHandledNamespace($attr_namespace_uri)
                && !$this->_xmlns->isValidAttributeNS($attr_namespace_uri, $local_name)) {
                throw new PHPTAL_ParserException("Attribute '$qname' is in '$attr_namespace_uri' namespace, but is not a supported PHPTAL attribute",
                            $this->file, $this->line);
            }

            $attrnodes[] = new PHPTAL_Dom_Attr($qname, $attr_namespace_uri, $value, $this->encoding);
        }

        $node = new PHPTAL_Dom_Element($element_qname, $namespace_uri, $attrnodes, $this->getXmlnsState());
        $this->pushNode($node);
        $this->_stack[] =  $this->_current;
        $this->_current = $node;
    }

    public function onElementData($data)
    {
        $this->pushNode(new PHPTAL_Dom_Text($data, $this->encoding));
    }

    public function onElementClose($qname)
    {
        if ($this->_current === $this->documentElement) {
            throw new PHPTAL_ParserException("Found closing tag for < $qname > where there are no open tags",
                        $this->file, $this->line);
        }
        if ($this->_current->getQualifiedName() != $qname) {
            throw new PHPTAL_ParserException("Tag closure mismatch, expected < /".$this->_current->getQualifiedName()." > (opened in line ".$this->_current->getSourceLine().") but found < /".$qname." >",
                        $this->file, $this->line);
        }
        $this->_current = array_pop($this->_stack);
        if ($this->_current instanceof PHPTAL_Dom_Element) {
            $this->_xmlns = $this->_current->getXmlnsState(); // restore namespace prefixes info to previous state
        }
    }

    private function pushNode(PHPTAL_Dom_Node $node)
    {
        $node->setSource($this->file, $this->line);
        $this->_current->appendChild($node);
    }

    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }
}