1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* This file is part of the Ariadne Component Library. |
4
|
|
|
* |
5
|
|
|
* (c) Muze <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace arc\xml; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* This class is a proxy for both the SimpleXMLElement and DOMElement |
15
|
|
|
* properties and methods. |
16
|
|
|
* @property \SimpleXMLElement nodeValue |
17
|
|
|
*/ |
18
|
|
|
class Proxy extends \ArrayObject implements DOMElement, SimpleXMLElement { |
19
|
|
|
|
20
|
|
|
use \arc\traits\Proxy { |
21
|
|
|
\arc\traits\Proxy::__construct as private ProxyConstruct; |
22
|
|
|
\arc\traits\Proxy::__call as private ProxyCall; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
private $parser = null; |
26
|
|
|
|
27
|
16 |
|
public function __construct( $node, $parser) { |
28
|
16 |
|
$this->ProxyConstruct( $node ); |
29
|
16 |
|
$this->parser = $parser; |
30
|
16 |
|
} |
31
|
|
|
|
32
|
2 |
|
public function __toString() { |
33
|
2 |
|
return isset($this->target) ? $this->target->asXML() : ''; |
34
|
|
|
} |
35
|
|
|
|
36
|
8 |
|
private function _isDomProperty( $name ) { |
37
|
|
|
$domProperties = [ |
38
|
8 |
|
'tagName', 'nodeType', 'parentNode', |
39
|
4 |
|
'firstChild', 'lastChild', 'previousSibling', 'nextSibling', |
40
|
4 |
|
'ownerDocument', 'namespaceURI', 'prefix', |
41
|
4 |
|
'localName', 'baseURI', 'textContent' |
42
|
4 |
|
]; |
43
|
8 |
|
return in_array( $name, $domProperties ); |
44
|
|
|
} |
45
|
|
|
|
46
|
8 |
|
private function _getTargetProperty($name) { |
47
|
8 |
|
$value = null; |
48
|
8 |
|
if ( $name[0] == '{' ) { |
49
|
2 |
|
list($ns, $name) = explode("}", $name); |
|
|
|
|
50
|
2 |
|
$ns = substr($ns, 1); |
|
|
|
|
51
|
2 |
|
$value = $this->target->children($ns, false)->{$name}; |
|
|
|
|
52
|
8 |
|
} else if ( strpos($name, ':')!== false ) { |
53
|
2 |
|
list ($ns, $name) = explode(":", $name); |
|
|
|
|
54
|
2 |
|
if ( isset($this->parser->namespaces[$ns]) ) { |
55
|
2 |
|
$ns = $this->parser->namespaces[$ns]; |
|
|
|
|
56
|
2 |
|
$prefix = false; |
57
|
1 |
|
} else { |
58
|
|
|
$prefix = true; |
59
|
|
|
} |
60
|
2 |
|
$value = $this->target->children($ns, $prefix)->{$name}; |
61
|
8 |
|
} else if ( !$this->_isDomProperty($name) ) { |
62
|
8 |
|
$value = $this->target->{$name}; |
63
|
4 |
|
} else { |
64
|
2 |
|
$dom = dom_import_simplexml($this->target); |
65
|
2 |
|
if ( isset($dom) ) { |
66
|
2 |
|
$value = $dom->{$name}; |
67
|
1 |
|
} |
68
|
|
|
} |
69
|
8 |
|
return $value; |
70
|
|
|
} |
71
|
|
|
|
72
|
16 |
|
private function _proxyResult( $value ) { |
73
|
16 |
|
if ( $value instanceof \DOMElement ) { |
74
|
2 |
|
$value = simplexml_import_dom($value); |
75
|
16 |
|
} else if ( $value instanceof \DOMNodeList ) { |
76
|
|
|
$array = []; |
77
|
|
|
for ( $i=0, $l=$value->length; $i<$l; $i ++ ) { |
78
|
|
|
$array[$i] = $value[$i]; |
79
|
|
|
} |
80
|
|
|
$value = $array; |
81
|
|
|
} |
82
|
16 |
|
if ( $value instanceof \SimpleXMLElement ) { |
83
|
16 |
|
$value = new static( $value, $this->parser ); |
84
|
15 |
|
} else if ( is_array($value) ) { |
85
|
8 |
|
foreach ( $value as $key => $subvalue ) { |
86
|
8 |
|
$value[$key] = $this->_proxyResult( $subvalue ); |
87
|
4 |
|
} |
88
|
4 |
|
} |
89
|
16 |
|
return $value; |
90
|
|
|
} |
91
|
|
|
|
92
|
14 |
|
public function __get( $name) { |
93
|
14 |
|
if ($name == 'nodeValue') { |
94
|
12 |
|
return $this->target; |
95
|
|
|
} |
96
|
8 |
|
return $this->_proxyResult( $this->_getTargetProperty($name) ); |
97
|
|
|
} |
98
|
|
|
|
99
|
4 |
|
private function _domCall( $name, $args ) { |
100
|
4 |
|
$dom = dom_import_simplexml($this->target); |
101
|
4 |
|
foreach ( $args as $index => $arg ) { |
102
|
4 |
|
if ( $arg instanceof \arc\xml\Proxy ) { |
103
|
2 |
|
$args[$index] = dom_import_simplexml( $arg->nodeValue ); |
104
|
3 |
|
} else if ( $arg instanceof \SimpleXMLElement ) { |
105
|
2 |
|
$args[$index] = dom_import_simplexml( $arg ); |
106
|
|
|
} |
107
|
2 |
|
} |
108
|
|
|
$importMethods = [ |
109
|
4 |
|
'appendChild', 'insertBefore', 'replaceChild' |
110
|
2 |
|
]; |
111
|
4 |
|
if ( in_array( $name, $importMethods ) ) { |
112
|
2 |
|
if ( isset($args[0]) && $args[0] instanceof \DOMNode ) { |
113
|
2 |
|
if ( $args[0]->ownerDocument !== $this->ownerDocument ) { |
|
|
|
|
114
|
2 |
|
$args[0] = $this->ownerDocument->importNode( $args[0], true); |
|
|
|
|
115
|
1 |
|
} |
116
|
1 |
|
} |
117
|
1 |
|
} |
118
|
4 |
|
return call_user_func_array( [ $dom, $name], $args ); |
119
|
|
|
} |
120
|
|
|
|
121
|
4 |
|
public function __call( $name, $args ) { |
122
|
4 |
|
if ( !method_exists( $this->target, $name ) ) { |
123
|
4 |
|
return $this->_proxyResult( $this->_domCall( $name, $args ) ); |
124
|
|
|
} else { |
125
|
4 |
|
return $this->_proxyResult( $this->ProxyCall( $name, $args ) ); |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Search through the XML DOM with a single CSS selector |
131
|
|
|
* @param string $query the CSS selector, most CSS 2 selectors work |
132
|
|
|
* @return Proxy |
133
|
|
|
*/ |
134
|
8 |
|
public function find( $query) { |
135
|
8 |
|
$xpath = \arc\xml::css2Xpath( $query ); |
136
|
8 |
|
return $this->_proxyResult( $this->target->xpath( $xpath ) ); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Searches through the subtree for an element with the given id and returns it |
141
|
|
|
* @param string $id |
142
|
|
|
* @return Proxy |
143
|
|
|
*/ |
144
|
2 |
|
public function getElementById( $id ) { |
145
|
2 |
|
return current($this->find('#'.$id)); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Register a namespace alias and URI to use in xpath and find |
150
|
|
|
* @param string $ns |
151
|
|
|
* @param string $uri |
|
|
|
|
152
|
|
|
*/ |
153
|
2 |
|
public function registerNamespace( $prefix, $ns ) { |
154
|
2 |
|
if ( $this->target && $this->target instanceof \SimpleXMLElement ) { |
155
|
2 |
|
$this->target->registerXPathNamespace($prefix, $ns); |
156
|
1 |
|
} |
157
|
2 |
|
$this->parser->namespaces[$prefix] = $ns; |
158
|
2 |
|
} |
159
|
|
|
|
160
|
4 |
|
public function offsetGet( $offset ) |
161
|
|
|
{ |
162
|
4 |
|
return (string) $this->target[$offset]; |
163
|
|
|
} |
164
|
|
|
|
165
|
2 |
|
public function offsetSet( $offset, $value ) |
166
|
|
|
{ |
167
|
2 |
|
$this->target[$offset] = $value; |
168
|
2 |
|
} |
169
|
|
|
|
170
|
|
|
public function offsetUnset( $offset ) |
171
|
|
|
{ |
172
|
|
|
unset( $this->target[$offset] ); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
PHP provides two ways to mark string literals. Either with single quotes
'literal'
or with double quotes"literal"
. The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (
\'
) and the backslash (\\
). Every other character is displayed as is.Double quoted string literals may contain other variables or more complex escape sequences.
will print an indented:
Single is Value
If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.
For more information on PHP string literals and available escape sequences see the PHP core documentation.