PageRenderTime 85ms CodeModel.GetById 19ms RepoModel.GetById 2ms app.codeStats 0ms

/wp-includes/atomlib.php

https://bitbucket.org/stephenharris/stephenharris
PHP | 368 lines | 236 code | 63 blank | 69 comment | 60 complexity | 83cf707b4178575abbcdf66d5e64ad2a MD5 | raw file
  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 <elias@torrez.us>
  9. * @version 0.4
  10. * @since 2.3.0
  11. */
  12. /**
  13. * Structure that store common Atom Feed Properties
  14. *
  15. * @package AtomLib
  16. */
  17. class AtomFeed {
  18. /**
  19. * Stores Links
  20. * @var array
  21. * @access public
  22. */
  23. var $links = array();
  24. /**
  25. * Stores Categories
  26. * @var array
  27. * @access public
  28. */
  29. var $categories = array();
  30. /**
  31. * Stores Entries
  32. *
  33. * @var array
  34. * @access public
  35. */
  36. var $entries = array();
  37. }
  38. /**
  39. * Structure that store Atom Entry Properties
  40. *
  41. * @package AtomLib
  42. */
  43. class AtomEntry {
  44. /**
  45. * Stores Links
  46. * @var array
  47. * @access public
  48. */
  49. var $links = array();
  50. /**
  51. * Stores Categories
  52. * @var array
  53. * @access public
  54. */
  55. var $categories = array();
  56. }
  57. /**
  58. * AtomLib Atom Parser API
  59. *
  60. * @package AtomLib
  61. */
  62. class AtomParser {
  63. var $NS = 'http://www.w3.org/2005/Atom';
  64. var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
  65. var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft');
  66. var $debug = false;
  67. var $depth = 0;
  68. var $indent = 2;
  69. var $in_content;
  70. var $ns_contexts = array();
  71. var $ns_decls = array();
  72. var $content_ns_decls = array();
  73. var $content_ns_contexts = array();
  74. var $is_xhtml = false;
  75. var $is_html = false;
  76. var $is_text = true;
  77. var $skipped_div = false;
  78. var $FILE = "php://input";
  79. var $feed;
  80. var $current;
  81. /**
  82. * PHP5 constructor.
  83. */
  84. function __construct() {
  85. $this->feed = new AtomFeed();
  86. $this->current = null;
  87. $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
  88. $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
  89. }
  90. /**
  91. * PHP4 constructor.
  92. */
  93. public function AtomParser() {
  94. self::__construct();
  95. }
  96. function _p($msg) {
  97. if($this->debug) {
  98. print str_repeat(" ", $this->depth * $this->indent) . $msg ."\n";
  99. }
  100. }
  101. function error_handler($log_level, $log_text, $error_file, $error_line) {
  102. $this->error = $log_text;
  103. }
  104. function parse() {
  105. set_error_handler(array(&$this, 'error_handler'));
  106. array_unshift($this->ns_contexts, array());
  107. if ( ! function_exists( 'xml_parser_create_ns' ) ) {
  108. trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) );
  109. return false;
  110. }
  111. $parser = xml_parser_create_ns();
  112. xml_set_object($parser, $this);
  113. xml_set_element_handler($parser, "start_element", "end_element");
  114. xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
  115. xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
  116. xml_set_character_data_handler($parser, "cdata");
  117. xml_set_default_handler($parser, "_default");
  118. xml_set_start_namespace_decl_handler($parser, "start_ns");
  119. xml_set_end_namespace_decl_handler($parser, "end_ns");
  120. $this->content = '';
  121. $ret = true;
  122. $fp = fopen($this->FILE, "r");
  123. while ($data = fread($fp, 4096)) {
  124. if($this->debug) $this->content .= $data;
  125. if(!xml_parse($parser, $data, feof($fp))) {
  126. /* translators: 1: error message, 2: line number */
  127. trigger_error(sprintf(__('XML Error: %1$s at line %2$s')."\n",
  128. xml_error_string(xml_get_error_code($parser)),
  129. xml_get_current_line_number($parser)));
  130. $ret = false;
  131. break;
  132. }
  133. }
  134. fclose($fp);
  135. xml_parser_free($parser);
  136. restore_error_handler();
  137. return $ret;
  138. }
  139. function start_element($parser, $name, $attrs) {
  140. $tag = array_pop(explode(":", $name));
  141. switch($name) {
  142. case $this->NS . ':feed':
  143. $this->current = $this->feed;
  144. break;
  145. case $this->NS . ':entry':
  146. $this->current = new AtomEntry();
  147. break;
  148. };
  149. $this->_p("start_element('$name')");
  150. #$this->_p(print_r($this->ns_contexts,true));
  151. #$this->_p('current(' . $this->current . ')');
  152. array_unshift($this->ns_contexts, $this->ns_decls);
  153. $this->depth++;
  154. if(!empty($this->in_content)) {
  155. $this->content_ns_decls = array();
  156. if($this->is_html || $this->is_text)
  157. trigger_error("Invalid content in element found. Content must not be of type text or html if it contains markup.");
  158. $attrs_prefix = array();
  159. // resolve prefixes for attributes
  160. foreach($attrs as $key => $value) {
  161. $with_prefix = $this->ns_to_prefix($key, true);
  162. $attrs_prefix[$with_prefix[1]] = $this->xml_escape($value);
  163. }
  164. $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
  165. if(strlen($attrs_str) > 0) {
  166. $attrs_str = " " . $attrs_str;
  167. }
  168. $with_prefix = $this->ns_to_prefix($name);
  169. if(!$this->is_declared_content_ns($with_prefix[0])) {
  170. array_push($this->content_ns_decls, $with_prefix[0]);
  171. }
  172. $xmlns_str = '';
  173. if(count($this->content_ns_decls) > 0) {
  174. array_unshift($this->content_ns_contexts, $this->content_ns_decls);
  175. $xmlns_str .= join(' ', array_map($this->map_xmlns_func, array_keys($this->content_ns_contexts[0]), array_values($this->content_ns_contexts[0])));
  176. if(strlen($xmlns_str) > 0) {
  177. $xmlns_str = " " . $xmlns_str;
  178. }
  179. }
  180. array_push($this->in_content, array($tag, $this->depth, "<". $with_prefix[1] ."{$xmlns_str}{$attrs_str}" . ">"));
  181. } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
  182. $this->in_content = array();
  183. $this->is_xhtml = $attrs['type'] == 'xhtml';
  184. $this->is_html = $attrs['type'] == 'html' || $attrs['type'] == 'text/html';
  185. $this->is_text = !in_array('type',array_keys($attrs)) || $attrs['type'] == 'text';
  186. $type = $this->is_xhtml ? 'XHTML' : ($this->is_html ? 'HTML' : ($this->is_text ? 'TEXT' : $attrs['type']));
  187. if(in_array('src',array_keys($attrs))) {
  188. $this->current->$tag = $attrs;
  189. } else {
  190. array_push($this->in_content, array($tag,$this->depth, $type));
  191. }
  192. } else if($tag == 'link') {
  193. array_push($this->current->links, $attrs);
  194. } else if($tag == 'category') {
  195. array_push($this->current->categories, $attrs);
  196. }
  197. $this->ns_decls = array();
  198. }
  199. function end_element($parser, $name) {
  200. $tag = array_pop(explode(":", $name));
  201. $ccount = count($this->in_content);
  202. # if we are *in* content, then let's proceed to serialize it
  203. if(!empty($this->in_content)) {
  204. # if we are ending the original content element
  205. # then let's finalize the content
  206. if($this->in_content[0][0] == $tag &&
  207. $this->in_content[0][1] == $this->depth) {
  208. $origtype = $this->in_content[0][2];
  209. array_shift($this->in_content);
  210. $newcontent = array();
  211. foreach($this->in_content as $c) {
  212. if(count($c) == 3) {
  213. array_push($newcontent, $c[2]);
  214. } else {
  215. if($this->is_xhtml || $this->is_text) {
  216. array_push($newcontent, $this->xml_escape($c));
  217. } else {
  218. array_push($newcontent, $c);
  219. }
  220. }
  221. }
  222. if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS)) {
  223. $this->current->$tag = array($origtype, join('',$newcontent));
  224. } else {
  225. $this->current->$tag = join('',$newcontent);
  226. }
  227. $this->in_content = array();
  228. } else if($this->in_content[$ccount-1][0] == $tag &&
  229. $this->in_content[$ccount-1][1] == $this->depth) {
  230. $this->in_content[$ccount-1][2] = substr($this->in_content[$ccount-1][2],0,-1) . "/>";
  231. } else {
  232. # else, just finalize the current element's content
  233. $endtag = $this->ns_to_prefix($name);
  234. array_push($this->in_content, array($tag, $this->depth, "</$endtag[1]>"));
  235. }
  236. }
  237. array_shift($this->ns_contexts);
  238. $this->depth--;
  239. if($name == ($this->NS . ':entry')) {
  240. array_push($this->feed->entries, $this->current);
  241. $this->current = null;
  242. }
  243. $this->_p("end_element('$name')");
  244. }
  245. function start_ns($parser, $prefix, $uri) {
  246. $this->_p("starting: " . $prefix . ":" . $uri);
  247. array_push($this->ns_decls, array($prefix,$uri));
  248. }
  249. function end_ns($parser, $prefix) {
  250. $this->_p("ending: #" . $prefix . "#");
  251. }
  252. function cdata($parser, $data) {
  253. $this->_p("data: #" . str_replace(array("\n"), array("\\n"), trim($data)) . "#");
  254. if(!empty($this->in_content)) {
  255. array_push($this->in_content, $data);
  256. }
  257. }
  258. function _default($parser, $data) {
  259. # when does this gets called?
  260. }
  261. function ns_to_prefix($qname, $attr=false) {
  262. # split 'http://www.w3.org/1999/xhtml:div' into ('http','//www.w3.org/1999/xhtml','div')
  263. $components = explode(":", $qname);
  264. # grab the last one (e.g 'div')
  265. $name = array_pop($components);
  266. if(!empty($components)) {
  267. # re-join back the namespace component
  268. $ns = join(":",$components);
  269. foreach($this->ns_contexts as $context) {
  270. foreach($context as $mapping) {
  271. if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
  272. return array($mapping, "$mapping[0]:$name");
  273. }
  274. }
  275. }
  276. }
  277. if($attr) {
  278. return array(null, $name);
  279. } else {
  280. foreach($this->ns_contexts as $context) {
  281. foreach($context as $mapping) {
  282. if(strlen($mapping[0]) == 0) {
  283. return array($mapping, $name);
  284. }
  285. }
  286. }
  287. }
  288. }
  289. function is_declared_content_ns($new_mapping) {
  290. foreach($this->content_ns_contexts as $context) {
  291. foreach($context as $mapping) {
  292. if($new_mapping == $mapping) {
  293. return true;
  294. }
  295. }
  296. }
  297. return false;
  298. }
  299. function xml_escape($string)
  300. {
  301. return str_replace(array('&','"',"'",'<','>'),
  302. array('&amp;','&quot;','&apos;','&lt;','&gt;'),
  303. $string );
  304. }
  305. }