xml::preamble()   A
last analyzed

Complexity

Conditions 6
Paths 10

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 9.5384

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 7
cts 13
cp 0.5385
rs 9.0111
c 0
b 0
f 0
cc 6
nc 10
nop 3
crap 9.5384
1
<?php
2
3
/*
4
 * This file is part of the Ariadne Component Library.
5
 *
6
 * (c) Muze <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace arc;
13
14
/**
15
 * Any method statically called on this class
16
 * will reroute the call to the XML writer instance at 
17
 * \arc\xml::$writer. Except for the methods:
18
 * parse, css2XPath, name, value, attribute, comment, cdata and preamble
19
 * If you need those call the Writer instance directly
20
 */
21
class xml
22
{
23
    /**
24
     * @var xml\Writer The writer instance to use by default
25
     */
26
    public static $writer = null;
27
28 6
    public static function __callStatic( $name, $args )
29
    {
30 6
        if ( !isset(static::$writer) ) {
31 2
            static::$writer = new xml\Writer();
32
        }
33 6
        return call_user_func_array( [ static::$writer, $name ], $args );
34
    }
35
36
    /**
37
     * This parses an XML string and returns a Proxy
38
     * @param string|Proxy $xml
39
     * @return Proxy
40
     * @throws \arc\UnknownError
41
     */
42 18
    public static function parse( $xml=null, $encoding = null )
43
    {
44 18
        $parser = new xml\Parser();
45 18
        return $parser->parse( $xml, $encoding );
46
    }
47
48
    /**
49
     * This method turns a single CSS 2 selector into an XPath query
50
     * @param string $cssSelector
51
     * @return string
52
     */
53 8
    public static function css2XPath( $cssSelector )
54
    {
55 8
        $tag = '(?:\w+\|)?\w+';
56
        /* based on work by Tijs Verkoyen - http://blog.verkoyen.eu/blog/p/detail/css-selector-to-xpath-query/ */
57
        $translateList = array(
58
            // E F: Matches any F element that is a descendant of an E element
59 8
            '/('.$tag.')\s+(?=(?:[^"]*"[^"]*")*[^"]*$)('.$tag.')/'
60 8
            => '\1//\2',
61
            // E > F: Matches any F element that is a child of an element E
62 8
            '/('.$tag.')\s*>\s*('.$tag.')/'
63 8
            => '\1/\2',
64
            // E:first-child: Matches element E when E is the first child of its parent
65 8
            '/('.$tag.'|(\w+\|)?\*):first-child/'
66 8
            => '*[1]/self::\1',
67
            // Matches E:checked, E:disabled or E:selected (and just for scrutinizer: this is not code!)
68 8
            '/('.$tag.'|(\w+\|)?\*):(checked|disabled|selected)/'
69 8
            => '\1 [ @\3 ]',
70
            // E + F: Matches any F element immediately preceded by an element
71 8
            '/('.$tag.')\s*\+\s*('.$tag.')/'
72 8
            => '\1/following-sibling::*[1]/self::\2',
73
            // E ~ F: Matches any F element preceded by an element
74 8
            '/('.$tag.')\s*\~\s*('.$tag.')/'
75 8
            => '\1/following-sibling::*/self::\2',
76
            // E[foo]: Matches any E element with the "foo" attribute set (whatever the value)
77 8
            '/('.$tag.')\[([\w\-]+)]/'
78 8
            => '\1 [ @\2 ]',
79
            // E[foo="warning"]: Matches any E element whose "foo" attribute value is exactly equal to "warning"
80 8
            '/('.$tag.')\[([\w\-]+)\=\"(.*)\"]/'
81 8
            => '\1[ contains( concat( " ", normalize-space(@\2), " " ), concat( " ", "\3", " " ) ) ]',
82
            // .warning: HTML only. The same as *[class~="warning"]
83
            '/(^|\s)\.([\w\-]+)+/'
84 8
            => '*[ contains( concat( " ", normalize-space(@class), " " ), concat( " ", "\2", " " ) ) ]',
85
            // div.warning: HTML only. The same as DIV[class~="warning"]
86 8
            '/('.$tag.'|(\w+\|)?\*)\.([\w\-]+)+/'
87 8
            => '\1[ contains( concat( " ", normalize-space(@class), " " ), concat( " ", "\3", " " ) ) ]',
88
            // E#myid: Matches any E element with id-attribute equal to "myid"
89 8
            '/('.$tag.')\#([\w\-]+)/'
90 8
            => "\\1[@id='\\2']",
91
            // #myid: Matches any E element with id-attribute equal to "myid"
92
            '/\#([\w\-]+)/'
93 8
            => "*[@id='\\1']",
94
            // namespace|
95
            '/(\w+)\|/'
96 8
            => "\\1:"
97
        );
98
99 8
        $cssSelectors = array_keys($translateList);
100 8
        $xPathQueries = array_values($translateList);
101
        do {
102 8
            $continue    = false;
103 8
            $cssSelector = (string) preg_replace($cssSelectors, $xPathQueries, $cssSelector);
104 8
            foreach ( $cssSelectors as $selector ) {
105 8
                if ( preg_match($selector, $cssSelector) ) {
106 2
                    $continue = true;
107 2
                    break;
108
                }
109
            }
110 8
        } while ( $continue );
111 8
        return '//'.$cssSelector;
112
    }
113
114
    /**
115
     * Returns a guaranteed valid XML name. Removes illegal characters from the name.
116
     * @param string $name
117
     * @return string
118
     */
119 6
    public static function name( $name)
120
    {
121 6
        return preg_replace( '/^[^:a-z_]*/isU', '',
122 6
            preg_replace( '/[^-.0-9:a-z_]/isU', '', $name
123
        ) );
124
    }
125
126
    /**
127
     * Returns a guaranteed valid XML attribute value. Removes illegal characters.
128
     * @param string|array|bool $value
129
     * @return string
130
     */
131 8
    public static function value( $value)
132
    {
133 8
        if (is_array( $value )) {
134
            $content = array_reduce( $value, function( $result, $value)
135
            {
136
                return $result . ' ' . static::value( $value );
137
            } );
138 8
        } else if (is_bool( $value )) {
139
            $content = $value ? 'true' : 'false';
140
        } else {
141 8
            $value = (string) $value;
142 8
            if (preg_match( '/^\s*<!\[CDATA\[/', $value )) {
143
                $content = $value;
144
            } else {
145 8
                $content = htmlspecialchars( (string) $value, ENT_QUOTES, 'UTF-8' );
146
            }
147
        }
148 8
        return $content;
149
    }
150
151
    /**
152
     * Returns a guaranteed valid XML attribute. Removes illegal characters.
153
     * @param string $name
154
     * @param string|array|bool $value
155
     * @return string
156
     */
157 6
    public static function attribute( $name, $value)
158
    {
159 6
        return ' ' . static::name( $name ) . '="' . static::value( $value ) . '"';
160
    }
161
162
    /**
163
     * Returns a guaranteed valid XML comment. Removes illegal characters.
164
     * @param string $content
165
     * @return string
166
     */
167 4
    public static function comment( $content)
168
    {
169 4
        return static::raw('<!-- ' . static::value( $content ) . ' -->');
170
    }
171
172
    /**
173
     * Returns a guaranteed valid XML CDATA string. Removes illegal characters.
174
     * @param string $content
175
     * @return string
176
     */
177 4
    public static function cdata( $content)
178
    {
179 4
        return static::raw('<![CDATA[' . str_replace( ']]>', ']]&gt;', $content ) . ']]>');
180
    }
181
182
    /**
183
     * Returns an XML preamble.
184
     * @param string $version Defaults to '1.0'
185
     * @param string $encoding Defaults to null
186
     * @param string $standalone Defaults to null
187
     * @return string
188
     */
189 2
    public static function preamble( $version = '1.0', $encoding = null, $standalone = null)
190
    {
191 2
        if (isset($standalone)) {
192
            if ($standalone === 'false') {
193
                $standalone = 'no';
194
            } else if ($standalone !== 'no') {
195
                $standalone = ( $standalone ? 'yes' : 'no' );
196
            }
197
            $standalone = static::attribute( 'standalone', $standalone );
198
        } else {
199 2
            $standalone = '';
200
        }
201 2
        $preamble = '<?xml version="' . static::value($version) . '"';
202 2
        if (isset( $encoding )) {
203
            $preamble .= ' " encoding="' . static::value($encoding) . '"';
204
        }
205 2
        $preamble .= $standalone . ' ?>';
206 2
        return $preamble;
207
    }
208
209 6
    public static function raw( $contents='' ) {
210 6
        return new xml\RawXML($contents);
211
    }
212
213
}