home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / atomlib.php < prev    next >
Encoding:
PHP Script  |  2016-12-12  |  11.6 KB  |  395 lines

  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. /**
  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' );
  98.         $this->map_xmlns_func = array( __CLASS__, 'map_xmlns' );
  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) {
  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) {
  141.         $this->error = $log_text;
  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 = '';
  166.  
  167.         $ret = true;
  168.  
  169.         $fp = fopen($this->FILE, "r");
  170.         while ($data = fread($fp, 4096)) {
  171.             if($this->debug) $this->content .= $data;
  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) {
  192.  
  193.         $tag = array_pop(explode(":", $name));
  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) {
  271.  
  272.         $tag = array_pop(explode(":", $name));
  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) {
  325.         $this->_p("starting: " . $prefix . ":" . $uri);
  326.         array_push($this->ns_decls, array($prefix,$uri));
  327.     }
  328.  
  329.     function end_ns($parser, $prefix) {
  330.         $this->_p("ending: #" . $prefix . "#");
  331.     }
  332.  
  333.     function cdata($parser, $data) {
  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) {
  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)
  389.     {
  390.              return str_replace(array('&','"',"'",'<','>'),
  391.                 array('&','"',''','<','>'),
  392.                 $string );
  393.     }
  394. }
  395.