Completed
Push — master ( b6c60c...0cde73 )
by Robbert
04:52
created

xml::raw()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 3
    public static function __callStatic( $name, $args )
29
    {
30 3
        if ( !isset(static::$writer) ) {
31 1
            static::$writer = new xml\Writer();
32 1
        }
33 3
        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\Exception
41
     */
42 7
    public static function parse( $xml=null, $encoding = null )
43
    {
44 6
        $parser = new xml\Parser();
45 7
        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 3
    public static function css2XPath( $cssSelector )
54 1
    {
55
        /* based on work by Tijs Verkoyen - http://blog.verkoyen.eu/blog/p/detail/css-selector-to-xpath-query/ */
56
        $translateList = array(
57
            // E F: Matches any F element that is a descendant of an E element
58
            '/(\w+)\s+(?=([^"]*"[^"]*")*[^"]*$)(\w+)/'
59 3
            => '\1//\3',
60
            // E > F: Matches any F element that is a child of an element E
61
            '/(\w+)\s*>\s*(\w+)/'
62 3
            => '\1/\2',
63
            // E:first-child: Matches element E when E is the first child of its parent
64
            '/(\w+|\*):first-child/'
65 3
            => '*[1]/self::\1',
66
            // Matches E:checked, E:disabled or E:selected (and just for scrutinizer: this is not code!)
67
            '/(\w+|\*):(checked|disabled|selected)/'
68 3
            => '\1 [ @\2 ]',
69
            // E + F: Matches any F element immediately preceded by an element
70
            '/(\w+)\s*\+\s*(\w+)/'
71 3
            => '\1/following-sibling::*[1]/self::\2',
72
            // E ~ F: Matches any F element preceded by an element
73
            '/(\w+)\s*\~\s*(\w+)/'
74 3
            => '\1/following-sibling::*/self::\2',
75
            // E[foo]: Matches any E element with the "foo" attribute set (whatever the value)
76
            '/(\w+)\[([\w\-]+)]/'
77 3
            => '\1 [ @\2 ]',
78
            // E[foo="warning"]: Matches any E element whose "foo" attribute value is exactly equal to "warning"
79
            '/(\w+)\[([\w\-]+)\=\"(.*)\"]/'
80 3
            => '\1[ contains( concat( " ", normalize-space(@\2), " " ), concat( " ", "\3", " " ) ) ]',
81
            // .warning: HTML only. The same as *[class~="warning"]
82
            '/(^|\s)\.([\w\-]+)+/'
83 3
            => '*[ contains( concat( " ", normalize-space(@class), " " ), concat( " ", "\2", " " ) ) ]',
84
            // div.warning: HTML only. The same as DIV[class~="warning"]
85
            '/(\w+|\*)\.([\w\-]+)+/'
86 3
            => '\1[ contains( concat( " ", normalize-space(@class), " " ), concat( " ", "\2", " " ) ) ]',
87
            // E#myid: Matches any E element with id-attribute equal to "myid"
88
            '/(\w+)+\#([\w\-]+)/'
89 3
            => "\\1[@id='\\2']",
90
            // #myid: Matches any E element with id-attribute equal to "myid"
91
            '/\#([\w\-]+)/'
92
            => "*[@id='\\1']"
93 3
        );
94
95 3
        $cssSelectors = array_keys($translateList);
96 3
        $xPathQueries = array_values($translateList);
97
        do {
98 3
            $continue    = false;
99 3
            $cssSelector = (string) preg_replace($cssSelectors, $xPathQueries, $cssSelector);
100 3
            foreach ( $cssSelectors as $selector ) {
101 3
                if ( preg_match($selector, $cssSelector) ) {
102 1
                    $continue = true;
103 1
                    break;
104
                }
105 3
            }
106 3
        } while ( $continue );
107 3
        return '//'.$cssSelector;
108
    }
109
110
    /**
111
     * Returns a guaranteed valid XML name. Removes illegal characters from the name.
112
     * @param string $name
113
     * @return string
114
     */
115 3
    public static function name( $name)
116
    {
117 3
        return preg_replace( '/^[^:a-z_]*/isU', '',
118 3
            preg_replace( '/[^-.0-9:a-z_]/isU', '', $name
119 3
        ) );
120
    }
121
122
    /**
123
     * Returns a guaranteed valid XML attribute value. Removes illegal characters.
124
     * @param string|array|bool $value
125
     * @return string
126
     */
127 4
    public static function value( $value)
128
    {
129 4
        if (is_array( $value )) {
130
            $content = array_reduce( $value, function( $result, $value)
131
            {
132
                return $result . ' ' . static::value( $value );
133
            } );
134 4
        } else if (is_bool( $value )) {
135
            $content = $value ? 'true' : 'false';
136
        } else {
137 4
            $value = (string) $value;
138 4
            if (preg_match( '/^\s*<!\[CDATA\[/', $value )) {
139
                $content = $value;
140
            } else {
141 4
                $content = htmlspecialchars( (string) $value, ENT_QUOTES, 'UTF-8' );
142
            }
143
        }
144 4
        return $content;
145
    }
146
147
    /**
148
     * Returns a guaranteed valid XML attribute. Removes illegal characters.
149
     * @param string $name
150
     * @param string|array|bool $value
151
     * @return string
152
     */
153 3
    public static function attribute( $name, $value)
154
    {
155 3
        return ' ' . static::name( $name ) . '="' . static::value( $value ) . '"';
156
    }
157
158
    /**
159
     * Returns a guaranteed valid XML comment. Removes illegal characters.
160
     * @param string $content
161
     * @return string
162
     */
163 2
    public static function comment( $content)
164
    {
165 2
        return static::raw('<!-- ' . static::value( $content ) . ' -->');
166
    }
167
168
    /**
169
     * Returns a guaranteed valid XML CDATA string. Removes illegal characters.
170
     * @param string $content
171
     * @return string
172
     */
173 2
    public static function cdata( $content)
174
    {
175 2
        return static::raw('<![CDATA[' . str_replace( ']]>', ']]&gt;', $content ) . ']]>');
176
    }
177
178
    /**
179
     * Returns an XML preamble.
180
     * @param string $version Defaults to '1.0'
181
     * @param string $encoding Defaults to null
182
     * @param string $standalone Defaults to null
183
     * @return string
184
     */
185 1
    public static function preamble( $version = '1.0', $encoding = null, $standalone = null)
186
    {
187 1
        if (isset($standalone)) {
188
            if ($standalone === 'false') {
189
                $standalone = 'no';
190
            } else if ($standalone !== 'no') {
191
                $standalone = ( $standalone ? 'yes' : 'no' );
192
            }
193
            $standalone = static::attribute( 'standalone', $standalone );
194
        } else {
195 1
            $standalone = '';
196
        }
197 1
        $preamble = '<?xml version="' . static::value($version) . '"';
198 1
        if (isset( $encoding )) {
199
            $preamble .= ' " encoding="' . static::value($encoding) . '"';
200
        }
201 1
        $preamble .= $standalone . ' ?>';
202 1
        return $preamble;
203
    }
204
205 3
    public static function raw( $contents='' ) {
206 3
        return new xml\RawXML($contents);
207
    }
208
209
}