PageRenderTime 72ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/openid_192/Services/Yadis/XRDS.php

https://bitbucket.org/pombredanne/spip-zone-treemap
PHP | 425 lines | 209 code | 68 blank | 148 comment | 33 complexity | 7d5bc2015021a37e453e5217f4c2fd5e MD5 | raw file
  1. <?php
  2. /**
  3. * This module contains the XRDS parsing code.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE: See the COPYING file included in this distribution.
  8. *
  9. * @package Yadis
  10. * @author JanRain, Inc. <openid@janrain.com>
  11. * @copyright 2005 Janrain, Inc.
  12. * @license http://www.gnu.org/copyleft/lesser.html LGPL
  13. */
  14. /**
  15. * Require the XPath implementation.
  16. */
  17. require_once 'Services/Yadis/XML.php';
  18. /**
  19. * This match mode means a given service must match ALL filters passed
  20. * to the Services_Yadis_XRDS::services() call.
  21. */
  22. define('SERVICES_YADIS_MATCH_ALL', 101);
  23. /**
  24. * This match mode means a given service must match ANY filters (at
  25. * least one) passed to the Services_Yadis_XRDS::services() call.
  26. */
  27. define('SERVICES_YADIS_MATCH_ANY', 102);
  28. /**
  29. * The priority value used for service elements with no priority
  30. * specified.
  31. */
  32. define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30));
  33. function Services_Yadis_getNSMap()
  34. {
  35. return array('xrds' => 'xri://$xrds',
  36. 'xrd' => 'xri://$xrd*($v*2.0)');
  37. }
  38. /**
  39. * @access private
  40. */
  41. function Services_Yadis_array_scramble($arr)
  42. {
  43. $result = array();
  44. while (count($arr)) {
  45. $index = array_rand($arr, 1);
  46. $result[] = $arr[$index];
  47. unset($arr[$index]);
  48. }
  49. return $result;
  50. }
  51. /**
  52. * This class represents a <Service> element in an XRDS document.
  53. * Objects of this type are returned by
  54. * Services_Yadis_XRDS::services() and
  55. * Services_Yadis_Yadis::services(). Each object corresponds directly
  56. * to a <Service> element in the XRDS and supplies a
  57. * getElements($name) method which you should use to inspect the
  58. * element's contents. See {@link Services_Yadis_Yadis} for more
  59. * information on the role this class plays in Yadis discovery.
  60. *
  61. * @package Yadis
  62. */
  63. class Services_Yadis_Service {
  64. /**
  65. * Creates an empty service object.
  66. */
  67. function Services_Yadis_Service()
  68. {
  69. $this->element = null;
  70. $this->parser = null;
  71. }
  72. /**
  73. * Return the URIs in the "Type" elements, if any, of this Service
  74. * element.
  75. *
  76. * @return array $type_uris An array of Type URI strings.
  77. */
  78. function getTypes()
  79. {
  80. $t = array();
  81. foreach ($this->getElements('xrd:Type') as $elem) {
  82. $c = $this->parser->content($elem);
  83. if ($c) {
  84. $t[] = $c;
  85. }
  86. }
  87. return $t;
  88. }
  89. /**
  90. * Return the URIs in the "URI" elements, if any, of this Service
  91. * element. The URIs are returned sorted in priority order.
  92. *
  93. * @return array $uris An array of URI strings.
  94. */
  95. function getURIs()
  96. {
  97. $uris = array();
  98. $last = array();
  99. foreach ($this->getElements('xrd:URI') as $elem) {
  100. $uri_string = $this->parser->content($elem);
  101. $attrs = $this->parser->attributes($elem);
  102. if ($attrs &&
  103. array_key_exists('priority', $attrs)) {
  104. $priority = intval($attrs['priority']);
  105. if (!array_key_exists($priority, $uris)) {
  106. $uris[$priority] = array();
  107. }
  108. $uris[$priority][] = $uri_string;
  109. } else {
  110. $last[] = $uri_string;
  111. }
  112. }
  113. $keys = array_keys($uris);
  114. sort($keys);
  115. // Rebuild array of URIs.
  116. $result = array();
  117. foreach ($keys as $k) {
  118. $new_uris = Services_Yadis_array_scramble($uris[$k]);
  119. $result = array_merge($result, $new_uris);
  120. }
  121. $result = array_merge($result,
  122. Services_Yadis_array_scramble($last));
  123. return $result;
  124. }
  125. /**
  126. * Returns the "priority" attribute value of this <Service>
  127. * element, if the attribute is present. Returns null if not.
  128. *
  129. * @return mixed $result Null or integer, depending on whether
  130. * this Service element has a 'priority' attribute.
  131. */
  132. function getPriority()
  133. {
  134. $attributes = $this->parser->attributes($this->element);
  135. if (array_key_exists('priority', $attributes)) {
  136. return intval($attributes['priority']);
  137. }
  138. return null;
  139. }
  140. /**
  141. * Used to get XML elements from this object's <Service> element.
  142. *
  143. * This is what you should use to get all custom information out
  144. * of this element. This is used by service filter functions to
  145. * determine whether a service element contains specific tags,
  146. * etc. NOTE: this only considers elements which are direct
  147. * children of the <Service> element for this object.
  148. *
  149. * @param string $name The name of the element to look for
  150. * @return array $list An array of elements with the specified
  151. * name which are direct children of the <Service> element. The
  152. * nodes returned by this function can be passed to $this->parser
  153. * methods (see {@link Services_Yadis_XMLParser}).
  154. */
  155. function getElements($name)
  156. {
  157. return $this->parser->evalXPath($name, $this->element);
  158. }
  159. }
  160. /**
  161. * This class performs parsing of XRDS documents.
  162. *
  163. * You should not instantiate this class directly; rather, call
  164. * parseXRDS statically:
  165. *
  166. * <pre> $xrds = Services_Yadis_XRDS::parseXRDS($xml_string);</pre>
  167. *
  168. * If the XRDS can be parsed and is valid, an instance of
  169. * Services_Yadis_XRDS will be returned. Otherwise, null will be
  170. * returned. This class is used by the Services_Yadis_Yadis::discover
  171. * method.
  172. *
  173. * @package Yadis
  174. */
  175. class Services_Yadis_XRDS {
  176. /**
  177. * Instantiate a Services_Yadis_XRDS object. Requires an XPath
  178. * instance which has been used to parse a valid XRDS document.
  179. */
  180. function Services_Yadis_XRDS(&$xmlParser, &$xrdNodes)
  181. {
  182. $this->parser =& $xmlParser;
  183. $this->xrdNode = $xrdNodes[count($xrdNodes) - 1];
  184. $this->allXrdNodes =& $xrdNodes;
  185. $this->serviceList = array();
  186. $this->_parse();
  187. }
  188. /**
  189. * Parse an XML string (XRDS document) and return either a
  190. * Services_Yadis_XRDS object or null, depending on whether the
  191. * XRDS XML is valid.
  192. *
  193. * @param string $xml_string An XRDS XML string.
  194. * @return mixed $xrds An instance of Services_Yadis_XRDS or null,
  195. * depending on the validity of $xml_string
  196. */
  197. function &parseXRDS($xml_string, $extra_ns_map = null)
  198. {
  199. $_null = null;
  200. if (!$xml_string) {
  201. return $_null;
  202. }
  203. $parser = Services_Yadis_getXMLParser();
  204. $ns_map = Services_Yadis_getNSMap();
  205. if ($extra_ns_map && is_array($extra_ns_map)) {
  206. $ns_map = array_merge($ns_map, $extra_ns_map);
  207. }
  208. if (!($parser && $parser->init($xml_string, $ns_map))) {
  209. return $_null;
  210. }
  211. // Try to get root element.
  212. $root = $parser->evalXPath('/xrds:XRDS[1]');
  213. if (!$root) {
  214. return $_null;
  215. }
  216. if (is_array($root)) {
  217. $root = $root[0];
  218. }
  219. $attrs = $parser->attributes($root);
  220. if (array_key_exists('xmlns:xrd', $attrs) &&
  221. $attrs['xmlns:xrd'] != 'xri://$xrd*($v*2.0)') {
  222. return $_null;
  223. } else if (array_key_exists('xmlns', $attrs) &&
  224. preg_match('/xri/', $attrs['xmlns']) &&
  225. $attrs['xmlns'] != 'xri://$xrd*($v*2.0)') {
  226. return $_null;
  227. }
  228. // Get the last XRD node.
  229. $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
  230. if (!$xrd_nodes) {
  231. return $_null;
  232. }
  233. $xrds = new Services_Yadis_XRDS($parser, $xrd_nodes);
  234. return $xrds;
  235. }
  236. /**
  237. * @access private
  238. */
  239. function _addService($priority, $service)
  240. {
  241. $priority = intval($priority);
  242. if (!array_key_exists($priority, $this->serviceList)) {
  243. $this->serviceList[$priority] = array();
  244. }
  245. $this->serviceList[$priority][] = $service;
  246. }
  247. /**
  248. * Creates the service list using nodes from the XRDS XML
  249. * document.
  250. *
  251. * @access private
  252. */
  253. function _parse()
  254. {
  255. $this->serviceList = array();
  256. $services = $this->parser->evalXPath('xrd:Service', $this->xrdNode);
  257. foreach ($services as $node) {
  258. $s =& new Services_Yadis_Service();
  259. $s->element = $node;
  260. $s->parser =& $this->parser;
  261. $priority = $s->getPriority();
  262. if ($priority === null) {
  263. $priority = SERVICES_YADIS_MAX_PRIORITY;
  264. }
  265. $this->_addService($priority, $s);
  266. }
  267. }
  268. /**
  269. * Returns a list of service objects which correspond to <Service>
  270. * elements in the XRDS XML document for this object.
  271. *
  272. * Optionally, an array of filter callbacks may be given to limit
  273. * the list of returned service objects. Furthermore, the default
  274. * mode is to return all service objects which match ANY of the
  275. * specified filters, but $filter_mode may be
  276. * SERVICES_YADIS_MATCH_ALL if you want to be sure that the
  277. * returned services match all the given filters. See {@link
  278. * Services_Yadis_Yadis} for detailed usage information on filter
  279. * functions.
  280. *
  281. * @param mixed $filters An array of callbacks to filter the
  282. * returned services, or null if all services are to be returned.
  283. * @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or
  284. * SERVICES_YADIS_MATCH_ANY, depending on whether the returned
  285. * services should match ALL or ANY of the specified filters,
  286. * respectively.
  287. * @return mixed $services An array of {@link
  288. * Services_Yadis_Service} objects if $filter_mode is a valid
  289. * mode; null if $filter_mode is an invalid mode (i.e., not
  290. * SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL).
  291. */
  292. function services($filters = null,
  293. $filter_mode = SERVICES_YADIS_MATCH_ANY)
  294. {
  295. $pri_keys = array_keys($this->serviceList);
  296. sort($pri_keys, SORT_NUMERIC);
  297. // If no filters are specified, return the entire service
  298. // list, ordered by priority.
  299. if (!$filters ||
  300. (!is_array($filters))) {
  301. $result = array();
  302. foreach ($pri_keys as $pri) {
  303. $result = array_merge($result, $this->serviceList[$pri]);
  304. }
  305. return $result;
  306. }
  307. // If a bad filter mode is specified, return null.
  308. if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY,
  309. SERVICES_YADIS_MATCH_ALL))) {
  310. return null;
  311. }
  312. // Otherwise, use the callbacks in the filter list to
  313. // determine which services are returned.
  314. $filtered = array();
  315. foreach ($pri_keys as $priority_value) {
  316. $service_obj_list = $this->serviceList[$priority_value];
  317. foreach ($service_obj_list as $service) {
  318. $matches = 0;
  319. foreach ($filters as $filter) {
  320. if (call_user_func_array($filter, array($service))) {
  321. $matches++;
  322. if ($filter_mode == SERVICES_YADIS_MATCH_ANY) {
  323. $pri = $service->getPriority();
  324. if ($pri === null) {
  325. $pri = SERVICES_YADIS_MAX_PRIORITY;
  326. }
  327. if (!array_key_exists($pri, $filtered)) {
  328. $filtered[$pri] = array();
  329. }
  330. $filtered[$pri][] = $service;
  331. break;
  332. }
  333. }
  334. }
  335. if (($filter_mode == SERVICES_YADIS_MATCH_ALL) &&
  336. ($matches == count($filters))) {
  337. $pri = $service->getPriority();
  338. if ($pri === null) {
  339. $pri = SERVICES_YADIS_MAX_PRIORITY;
  340. }
  341. if (!array_key_exists($pri, $filtered)) {
  342. $filtered[$pri] = array();
  343. }
  344. $filtered[$pri][] = $service;
  345. }
  346. }
  347. }
  348. $pri_keys = array_keys($filtered);
  349. sort($pri_keys, SORT_NUMERIC);
  350. $result = array();
  351. foreach ($pri_keys as $pri) {
  352. $result = array_merge($result, $filtered[$pri]);
  353. }
  354. return $result;
  355. }
  356. }
  357. ?>