PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/OpenID/Services/Yadis/XRDS.php

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