AtomParser::start_element()   F
last analyzed

Complexity

Conditions 21
Paths 273

Size

Total Lines 78
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 48
nc 273
nop 3
dl 0
loc 78
rs 3.8955
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Atom Syndication Format PHP Library
4
 *
5
 * @package AtomLib
6
 * @link http://code.google.com/p/phpatomlib/
7
 *
8
 * @author Elias Torres <[email protected]>
9
 * @version 0.4
10
 * @since 2.3.0
11
 */
12
13
/**
14
 * Structure that store common Atom Feed Properties
15
 *
16
 * @package AtomLib
17
 */
18
class AtomFeed {
19
	/**
20
	 * Stores Links
21
	 * @var array
22
	 * @access public
23
	 */
24
    var $links = array();
25
    /**
26
     * Stores Categories
27
     * @var array
28
     * @access public
29
     */
30
    var $categories = array();
31
	/**
32
	 * Stores Entries
33
	 *
34
	 * @var array
35
	 * @access public
36
	 */
37
    var $entries = array();
38
}
39
40
/**
41
 * Structure that store Atom Entry Properties
42
 *
43
 * @package AtomLib
44
 */
45
class AtomEntry {
46
	/**
47
	 * Stores Links
48
	 * @var array
49
	 * @access public
50
	 */
51
    var $links = array();
52
    /**
53
     * Stores Categories
54
     * @var array
55
	 * @access public
56
     */
57
    var $categories = array();
58
}
59
60
/**
61
 * AtomLib Atom Parser API
62
 *
63
 * @package AtomLib
64
 */
65
class AtomParser {
66
67
    var $NS = 'http://www.w3.org/2005/Atom';
68
    var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
69
    var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft');
70
71
    var $debug = false;
72
73
    var $depth = 0;
74
    var $indent = 2;
75
    var $in_content;
76
    var $ns_contexts = array();
77
    var $ns_decls = array();
78
    var $content_ns_decls = array();
79
    var $content_ns_contexts = array();
80
    var $is_xhtml = false;
81
    var $is_html = false;
82
    var $is_text = true;
83
    var $skipped_div = false;
84
85
    var $FILE = "php://input";
86
87
    var $feed;
88
    var $current;
89
90
	/**
91
	 * PHP5 constructor.
92
	 */
93
    function __construct() {
94
95
        $this->feed = new AtomFeed();
96
        $this->current = null;
97
        $this->map_attrs_func = array( __CLASS__, 'map_attrs' );
0 ignored issues
show
Bug introduced by
The property map_attrs_func does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
98
        $this->map_xmlns_func = array( __CLASS__, 'map_xmlns' );
0 ignored issues
show
Bug introduced by
The property map_xmlns_func does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
99
    }
100
101
	/**
102
	 * PHP4 constructor.
103
	 */
104
	public function AtomParser() {
105
		self::__construct();
106
	}
107
108
	/**
109
	 * Map attributes to key="val"
110
	 *
111
	 * @param string $k Key
112
	 * @param string $v Value
113
	 * @return string
114
	 */
115
	public static function map_attrs($k, $v) {
116
		return "$k=\"$v\"";
117
	}
118
119
	/**
120
	 * Map XML namespace to string.
121
	 *
122
	 * @param indexish $p XML Namespace element index
123
	 * @param array $n Two-element array pair. [ 0 => {namespace}, 1 => {url} ]
124
	 * @return string 'xmlns="{url}"' or 'xmlns:{namespace}="{url}"'
125
	 */
126
	public static function map_xmlns($p, $n) {
127
		$xd = "xmlns";
128
		if( 0 < strlen($n[0]) ) {
129
			$xd .= ":{$n[0]}";
130
		}
131
		return "{$xd}=\"{$n[1]}\"";
132
	}
133
134
    function _p($msg) {
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 3 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
135
        if($this->debug) {
136
            print str_repeat(" ", $this->depth * $this->indent) . $msg ."\n";
137
        }
138
    }
139
140
    function error_handler($log_level, $log_text, $error_file, $error_line) {
0 ignored issues
show
Unused Code introduced by
The parameter $error_file is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $error_line is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
141
        $this->error = $log_text;
0 ignored issues
show
Bug introduced by
The property error does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
142
    }
143
144
    function parse() {
145
146
        set_error_handler(array(&$this, 'error_handler'));
147
148
        array_unshift($this->ns_contexts, array());
149
150
        if ( ! function_exists( 'xml_parser_create_ns' ) ) {
151
        	trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) );
152
        	return false;
153
        }
154
155
        $parser = xml_parser_create_ns();
156
        xml_set_object($parser, $this);
157
        xml_set_element_handler($parser, "start_element", "end_element");
158
        xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
159
        xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
160
        xml_set_character_data_handler($parser, "cdata");
161
        xml_set_default_handler($parser, "_default");
162
        xml_set_start_namespace_decl_handler($parser, "start_ns");
163
        xml_set_end_namespace_decl_handler($parser, "end_ns");
164
165
        $this->content = '';
0 ignored issues
show
Bug introduced by
The property content does not seem to exist. Did you mean ATOM_CONTENT_ELEMENTS?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
166
167
        $ret = true;
168
169
        $fp = fopen($this->FILE, "r");
170
        while ($data = fread($fp, 4096)) {
171
            if($this->debug) $this->content .= $data;
0 ignored issues
show
Bug introduced by
The property content does not seem to exist. Did you mean ATOM_CONTENT_ELEMENTS?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
172
173
            if(!xml_parse($parser, $data, feof($fp))) {
174
                /* translators: 1: error message, 2: line number */
175
                trigger_error(sprintf(__('XML Error: %1$s at line %2$s')."\n",
176
                    xml_error_string(xml_get_error_code($parser)),
177
                    xml_get_current_line_number($parser)));
178
                $ret = false;
179
                break;
180
            }
181
        }
182
        fclose($fp);
183
184
        xml_parser_free($parser);
185
186
        restore_error_handler();
187
188
        return $ret;
189
    }
190
191
    function start_element($parser, $name, $attrs) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
192
193
        $tag = array_pop(explode(":", $name));
0 ignored issues
show
Bug introduced by
explode(':', $name) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
194
195
        switch($name) {
196
            case $this->NS . ':feed':
197
                $this->current = $this->feed;
198
                break;
199
            case $this->NS . ':entry':
200
                $this->current = new AtomEntry();
201
                break;
202
        };
203
204
        $this->_p("start_element('$name')");
205
        #$this->_p(print_r($this->ns_contexts,true));
206
        #$this->_p('current(' . $this->current . ')');
207
208
        array_unshift($this->ns_contexts, $this->ns_decls);
209
210
        $this->depth++;
211
212
        if(!empty($this->in_content)) {
213
214
            $this->content_ns_decls = array();
215
216
            if($this->is_html || $this->is_text)
217
                trigger_error("Invalid content in element found. Content must not be of type text or html if it contains markup.");
218
219
            $attrs_prefix = array();
220
221
            // resolve prefixes for attributes
222
            foreach($attrs as $key => $value) {
223
                $with_prefix = $this->ns_to_prefix($key, true);
224
                $attrs_prefix[$with_prefix[1]] = $this->xml_escape($value);
225
            }
226
227
            $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
228
            if(strlen($attrs_str) > 0) {
229
                $attrs_str = " " . $attrs_str;
230
            }
231
232
            $with_prefix = $this->ns_to_prefix($name);
233
234
            if(!$this->is_declared_content_ns($with_prefix[0])) {
235
                array_push($this->content_ns_decls, $with_prefix[0]);
236
            }
237
238
            $xmlns_str = '';
239
            if(count($this->content_ns_decls) > 0) {
240
                array_unshift($this->content_ns_contexts, $this->content_ns_decls);
241
                $xmlns_str .= join(' ', array_map($this->map_xmlns_func, array_keys($this->content_ns_contexts[0]), array_values($this->content_ns_contexts[0])));
242
                if(strlen($xmlns_str) > 0) {
243
                    $xmlns_str = " " . $xmlns_str;
244
                }
245
            }
246
247
            array_push($this->in_content, array($tag, $this->depth, "<". $with_prefix[1] ."{$xmlns_str}{$attrs_str}" . ">"));
248
249
        } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
250
            $this->in_content = array();
251
            $this->is_xhtml = $attrs['type'] == 'xhtml';
252
            $this->is_html = $attrs['type'] == 'html' || $attrs['type'] == 'text/html';
253
            $this->is_text = !in_array('type',array_keys($attrs)) || $attrs['type'] == 'text';
254
            $type = $this->is_xhtml ? 'XHTML' : ($this->is_html ? 'HTML' : ($this->is_text ? 'TEXT' : $attrs['type']));
255
256
            if(in_array('src',array_keys($attrs))) {
257
                $this->current->$tag = $attrs;
258
            } else {
259
                array_push($this->in_content, array($tag,$this->depth, $type));
260
            }
261
        } else if($tag == 'link') {
262
            array_push($this->current->links, $attrs);
263
        } else if($tag == 'category') {
264
            array_push($this->current->categories, $attrs);
265
        }
266
267
        $this->ns_decls = array();
268
    }
269
270
    function end_element($parser, $name) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
271
272
        $tag = array_pop(explode(":", $name));
0 ignored issues
show
Bug introduced by
explode(':', $name) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
273
274
        $ccount = count($this->in_content);
275
276
        # if we are *in* content, then let's proceed to serialize it
277
        if(!empty($this->in_content)) {
278
            # if we are ending the original content element
279
            # then let's finalize the content
280
            if($this->in_content[0][0] == $tag &&
281
                $this->in_content[0][1] == $this->depth) {
282
                $origtype = $this->in_content[0][2];
283
                array_shift($this->in_content);
284
                $newcontent = array();
285
                foreach($this->in_content as $c) {
286
                    if(count($c) == 3) {
287
                        array_push($newcontent, $c[2]);
288
                    } else {
289
                        if($this->is_xhtml || $this->is_text) {
290
                            array_push($newcontent, $this->xml_escape($c));
291
                        } else {
292
                            array_push($newcontent, $c);
293
                        }
294
                    }
295
                }
296
                if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS)) {
297
                    $this->current->$tag = array($origtype, join('',$newcontent));
298
                } else {
299
                    $this->current->$tag = join('',$newcontent);
300
                }
301
                $this->in_content = array();
302
            } else if($this->in_content[$ccount-1][0] == $tag &&
303
                $this->in_content[$ccount-1][1] == $this->depth) {
304
                $this->in_content[$ccount-1][2] = substr($this->in_content[$ccount-1][2],0,-1) . "/>";
305
            } else {
306
                # else, just finalize the current element's content
307
                $endtag = $this->ns_to_prefix($name);
308
                array_push($this->in_content, array($tag, $this->depth, "</$endtag[1]>"));
309
            }
310
        }
311
312
        array_shift($this->ns_contexts);
313
314
        $this->depth--;
315
316
        if($name == ($this->NS . ':entry')) {
317
            array_push($this->feed->entries, $this->current);
318
            $this->current = null;
319
        }
320
321
        $this->_p("end_element('$name')");
322
    }
323
324
    function start_ns($parser, $prefix, $uri) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
325
        $this->_p("starting: " . $prefix . ":" . $uri);
326
        array_push($this->ns_decls, array($prefix,$uri));
327
    }
328
329
    function end_ns($parser, $prefix) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
330
        $this->_p("ending: #" . $prefix . "#");
331
    }
332
333
    function cdata($parser, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
334
        $this->_p("data: #" . str_replace(array("\n"), array("\\n"), trim($data)) . "#");
335
        if(!empty($this->in_content)) {
336
            array_push($this->in_content, $data);
337
        }
338
    }
339
340
    function _default($parser, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
341
        # when does this gets called?
342
    }
343
344
345
    function ns_to_prefix($qname, $attr=false) {
346
        # split 'http://www.w3.org/1999/xhtml:div' into ('http','//www.w3.org/1999/xhtml','div')
347
        $components = explode(":", $qname);
348
349
        # grab the last one (e.g 'div')
350
        $name = array_pop($components);
351
352
        if(!empty($components)) {
353
            # re-join back the namespace component
354
            $ns = join(":",$components);
355
            foreach($this->ns_contexts as $context) {
356
                foreach($context as $mapping) {
357
                    if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
358
                        return array($mapping, "$mapping[0]:$name");
359
                    }
360
                }
361
            }
362
        }
363
364
        if($attr) {
365
            return array(null, $name);
366
        } else {
367
            foreach($this->ns_contexts as $context) {
368
                foreach($context as $mapping) {
369
                    if(strlen($mapping[0]) == 0) {
370
                        return array($mapping, $name);
371
                    }
372
                }
373
            }
374
        }
375
    }
376
377
    function is_declared_content_ns($new_mapping) {
378
        foreach($this->content_ns_contexts as $context) {
379
            foreach($context as $mapping) {
380
                if($new_mapping == $mapping) {
381
                    return true;
382
                }
383
            }
384
        }
385
        return false;
386
    }
387
388
    function xml_escape($string)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
389
    {
390
             return str_replace(array('&','"',"'",'<','>'),
391
                array('&amp;','&quot;','&apos;','&lt;','&gt;'),
392
                $string );
393
    }
394
}
395