Completed
Push — master ( 0cde73...91f8fc )
by Auke
01:54
created

Proxy::_proxyResult()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8.8142

Importance

Changes 2
Bugs 1 Features 2
Metric Value
c 2
b 1
f 2
dl 0
loc 19
ccs 12
cts 18
cp 0.6667
rs 8.2222
cc 7
eloc 14
nc 9
nop 1
crap 8.8142
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);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 14 spaces but found 4 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
51 2
            $value = $this->target->children($ns, false)->{$name};
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
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 ) {
0 ignored issues
show
Documentation introduced by
The property ownerDocument does not exist on object<arc\xml\Proxy>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
114 2
                    $args[0] = $this->ownerDocument->importNode( $args[0], true);
0 ignored issues
show
Documentation introduced by
The property ownerDocument does not exist on object<arc\xml\Proxy>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 $prefix the alias for this namespace
151
     * @param string $ns the URI for this namespace
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