42 |
aurelien |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
/**
|
|
|
4 |
* The core PHP Yadis implementation.
|
|
|
5 |
*
|
|
|
6 |
* PHP versions 4 and 5
|
|
|
7 |
*
|
|
|
8 |
* LICENSE: See the COPYING file included in this distribution.
|
|
|
9 |
*
|
|
|
10 |
* @package Yadis
|
|
|
11 |
* @author JanRain, Inc. <openid@janrain.com>
|
|
|
12 |
* @copyright 2005 Janrain, Inc.
|
|
|
13 |
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
|
|
14 |
*/
|
|
|
15 |
|
|
|
16 |
/**
|
|
|
17 |
* Need both fetcher types so we can use the right one based on the
|
|
|
18 |
* presence or absence of CURL.
|
|
|
19 |
*/
|
|
|
20 |
require_once "Services/Yadis/PlainHTTPFetcher.php";
|
|
|
21 |
require_once "Services/Yadis/ParanoidHTTPFetcher.php";
|
|
|
22 |
|
|
|
23 |
/**
|
|
|
24 |
* Need this for parsing HTML (looking for META tags).
|
|
|
25 |
*/
|
|
|
26 |
require_once "Services/Yadis/ParseHTML.php";
|
|
|
27 |
|
|
|
28 |
/**
|
|
|
29 |
* Need this to parse the XRDS document during Yadis discovery.
|
|
|
30 |
*/
|
|
|
31 |
require_once "Services/Yadis/XRDS.php";
|
|
|
32 |
|
|
|
33 |
/**
|
|
|
34 |
* This is the core of the PHP Yadis library. This is the only class
|
|
|
35 |
* a user needs to use to perform Yadis discovery. This class
|
|
|
36 |
* performs the discovery AND stores the result of the discovery.
|
|
|
37 |
*
|
|
|
38 |
* First, require this library into your program source:
|
|
|
39 |
*
|
|
|
40 |
* <pre> require_once "Services/Yadis/Yadis.php";</pre>
|
|
|
41 |
*
|
|
|
42 |
* To perform Yadis discovery, first call the "discover" method
|
|
|
43 |
* statically with a URI parameter:
|
|
|
44 |
*
|
|
|
45 |
* <pre> $http_response = array();
|
|
|
46 |
* $fetcher = Services_Yadis_Yadis::getHTTPFetcher();
|
|
|
47 |
* $yadis_object = Services_Yadis_Yadis::discover($uri,
|
|
|
48 |
* $http_response, $fetcher);</pre>
|
|
|
49 |
*
|
|
|
50 |
* If the discovery succeeds, $yadis_object will be an instance of
|
|
|
51 |
* {@link Services_Yadis_Yadis}. If not, it will be null. The XRDS
|
|
|
52 |
* document found during discovery should have service descriptions,
|
|
|
53 |
* which can be accessed by calling
|
|
|
54 |
*
|
|
|
55 |
* <pre> $service_list = $yadis_object->services();</pre>
|
|
|
56 |
*
|
|
|
57 |
* which returns an array of objects which describe each service.
|
|
|
58 |
* These objects are instances of Services_Yadis_Service. Each object
|
|
|
59 |
* describes exactly one whole Service element, complete with all of
|
|
|
60 |
* its Types and URIs (no expansion is performed). The common use
|
|
|
61 |
* case for using the service objects returned by services() is to
|
|
|
62 |
* write one or more filter functions and pass those to services():
|
|
|
63 |
*
|
|
|
64 |
* <pre> $service_list = $yadis_object->services(
|
|
|
65 |
* array("filterByURI",
|
|
|
66 |
* "filterByExtension"));</pre>
|
|
|
67 |
*
|
|
|
68 |
* The filter functions (whose names appear in the array passed to
|
|
|
69 |
* services()) take the following form:
|
|
|
70 |
*
|
|
|
71 |
* <pre> function myFilter(&$service) {
|
|
|
72 |
* // Query $service object here. Return true if the service
|
|
|
73 |
* // matches your query; false if not.
|
|
|
74 |
* }</pre>
|
|
|
75 |
*
|
|
|
76 |
* This is an example of a filter which uses a regular expression to
|
|
|
77 |
* match the content of URI tags (note that the Services_Yadis_Service
|
|
|
78 |
* class provides a getURIs() method which you should use instead of
|
|
|
79 |
* this contrived example):
|
|
|
80 |
*
|
|
|
81 |
* <pre>
|
|
|
82 |
* function URIMatcher(&$service) {
|
|
|
83 |
* foreach ($service->getElements('xrd:URI') as $uri) {
|
|
|
84 |
* if (preg_match("/some_pattern/",
|
|
|
85 |
* $service->parser->content($uri))) {
|
|
|
86 |
* return true;
|
|
|
87 |
* }
|
|
|
88 |
* }
|
|
|
89 |
* return false;
|
|
|
90 |
* }</pre>
|
|
|
91 |
*
|
|
|
92 |
* The filter functions you pass will be called for each service
|
|
|
93 |
* object to determine which ones match the criteria your filters
|
|
|
94 |
* specify. The default behavior is that if a given service object
|
|
|
95 |
* matches ANY of the filters specified in the services() call, it
|
|
|
96 |
* will be returned. You can specify that a given service object will
|
|
|
97 |
* be returned ONLY if it matches ALL specified filters by changing
|
|
|
98 |
* the match mode of services():
|
|
|
99 |
*
|
|
|
100 |
* <pre> $yadis_object->services(array("filter1", "filter2"),
|
|
|
101 |
* SERVICES_YADIS_MATCH_ALL);</pre>
|
|
|
102 |
*
|
|
|
103 |
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link
|
|
|
104 |
* SERVICES_YADIS_MATCH_ANY}.
|
|
|
105 |
*
|
|
|
106 |
* Services described in an XRDS should have a library which you'll
|
|
|
107 |
* probably be using. Those libraries are responsible for defining
|
|
|
108 |
* filters that can be used with the "services()" call. If you need
|
|
|
109 |
* to write your own filter, see the documentation for {@link
|
|
|
110 |
* Services_Yadis_Service}.
|
|
|
111 |
*
|
|
|
112 |
* @package Yadis
|
|
|
113 |
*/
|
|
|
114 |
class Services_Yadis_Yadis {
|
|
|
115 |
|
|
|
116 |
/**
|
|
|
117 |
* Returns an HTTP fetcher object. If the CURL extension is
|
|
|
118 |
* present, an instance of {@link Services_Yadis_ParanoidHTTPFetcher}
|
|
|
119 |
* is returned. If not, an instance of
|
|
|
120 |
* {@link Services_Yadis_PlainHTTPFetcher} is returned.
|
|
|
121 |
*/
|
|
|
122 |
function getHTTPFetcher($timeout = 20)
|
|
|
123 |
{
|
|
|
124 |
if (Services_Yadis_Yadis::curlPresent()) {
|
|
|
125 |
$fetcher = new Services_Yadis_ParanoidHTTPFetcher($timeout);
|
|
|
126 |
} else {
|
|
|
127 |
$fetcher = new Services_Yadis_PlainHTTPFetcher($timeout);
|
|
|
128 |
}
|
|
|
129 |
return $fetcher;
|
|
|
130 |
}
|
|
|
131 |
|
|
|
132 |
function curlPresent()
|
|
|
133 |
{
|
|
|
134 |
return function_exists('curl_init');
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
/**
|
|
|
138 |
* @access private
|
|
|
139 |
*/
|
|
|
140 |
function _getHeader($header_list, $names)
|
|
|
141 |
{
|
|
|
142 |
foreach ($header_list as $name => $value) {
|
|
|
143 |
foreach ($names as $n) {
|
|
|
144 |
if (strtolower($name) == strtolower($n)) {
|
|
|
145 |
return $value;
|
|
|
146 |
}
|
|
|
147 |
}
|
|
|
148 |
}
|
|
|
149 |
|
|
|
150 |
return null;
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
/**
|
|
|
154 |
* @access private
|
|
|
155 |
*/
|
|
|
156 |
function _getContentType($content_type_header)
|
|
|
157 |
{
|
|
|
158 |
if ($content_type_header) {
|
|
|
159 |
$parts = explode(";", $content_type_header);
|
|
|
160 |
return strtolower($parts[0]);
|
|
|
161 |
}
|
|
|
162 |
}
|
|
|
163 |
|
|
|
164 |
/**
|
|
|
165 |
* This should be called statically and will build a Yadis
|
|
|
166 |
* instance if the discovery process succeeds. This implements
|
|
|
167 |
* Yadis discovery as specified in the Yadis specification.
|
|
|
168 |
*
|
|
|
169 |
* @param string $uri The URI on which to perform Yadis discovery.
|
|
|
170 |
*
|
|
|
171 |
* @param array $http_response An array reference where the HTTP
|
|
|
172 |
* response object will be stored (see {@link
|
|
|
173 |
* Services_Yadis_HTTPResponse}.
|
|
|
174 |
*
|
|
|
175 |
* @param Services_Yadis_HTTPFetcher $fetcher An instance of a
|
|
|
176 |
* Services_Yadis_HTTPFetcher subclass.
|
|
|
177 |
*
|
|
|
178 |
* @param array $extra_ns_map An array which maps namespace names
|
|
|
179 |
* to namespace URIs to be used when parsing the Yadis XRDS
|
|
|
180 |
* document.
|
|
|
181 |
*
|
|
|
182 |
* @param integer $timeout An optional fetcher timeout, in seconds.
|
|
|
183 |
*
|
|
|
184 |
* @return mixed $obj Either null or an instance of
|
|
|
185 |
* Services_Yadis_Yadis, depending on whether the discovery
|
|
|
186 |
* succeeded.
|
|
|
187 |
*/
|
|
|
188 |
function discover($uri, &$http_response, &$fetcher,
|
|
|
189 |
$extra_ns_map = null, $timeout = 20)
|
|
|
190 |
{
|
|
|
191 |
if (!$uri) {
|
|
|
192 |
return null;
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
$request_uri = $uri;
|
|
|
196 |
$headers = array("Accept: application/xrds+xml");
|
|
|
197 |
|
|
|
198 |
if (!$fetcher) {
|
|
|
199 |
$fetcher = Services_Yadis_Yadis::getHTTPFetcher($timeout);
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
$response = $fetcher->get($uri, $headers);
|
|
|
203 |
$http_response = $response;
|
|
|
204 |
|
|
|
205 |
if (!$response) {
|
|
|
206 |
return null;
|
|
|
207 |
}
|
|
|
208 |
|
|
|
209 |
if ($response->status != 200) {
|
|
|
210 |
return null;
|
|
|
211 |
}
|
|
|
212 |
|
|
|
213 |
$xrds_uri = $response->final_url;
|
|
|
214 |
$uri = $response->final_url;
|
|
|
215 |
$body = $response->body;
|
|
|
216 |
|
|
|
217 |
$xrds_header_uri = Services_Yadis_Yadis::_getHeader(
|
|
|
218 |
$response->headers,
|
|
|
219 |
array('x-xrds-location',
|
|
|
220 |
'x-yadis-location'));
|
|
|
221 |
|
|
|
222 |
$content_type = Services_Yadis_Yadis::_getHeader($response->headers,
|
|
|
223 |
array('content-type'));
|
|
|
224 |
|
|
|
225 |
if ($xrds_header_uri) {
|
|
|
226 |
$xrds_uri = $xrds_header_uri;
|
|
|
227 |
$response = $fetcher->get($xrds_uri);
|
|
|
228 |
$http_response = $response;
|
|
|
229 |
if (!$response) {
|
|
|
230 |
return null;
|
|
|
231 |
} else {
|
|
|
232 |
$body = $response->body;
|
|
|
233 |
$headers = $response->headers;
|
|
|
234 |
$content_type = Services_Yadis_Yadis::_getHeader($headers,
|
|
|
235 |
array('content-type'));
|
|
|
236 |
}
|
|
|
237 |
}
|
|
|
238 |
|
|
|
239 |
if (Services_Yadis_Yadis::_getContentType($content_type) !=
|
|
|
240 |
'application/xrds+xml') {
|
|
|
241 |
// Treat the body as HTML and look for a META tag.
|
|
|
242 |
$parser = new Services_Yadis_ParseHTML();
|
|
|
243 |
$new_uri = $parser->getHTTPEquiv($body);
|
|
|
244 |
$xrds_uri = null;
|
|
|
245 |
if ($new_uri) {
|
|
|
246 |
$response = $fetcher->get($new_uri);
|
|
|
247 |
if ($response->status != 200) {
|
|
|
248 |
return null;
|
|
|
249 |
}
|
|
|
250 |
$http_response = $response;
|
|
|
251 |
$body = $response->body;
|
|
|
252 |
$xrds_uri = $new_uri;
|
|
|
253 |
$content_type = Services_Yadis_Yadis::_getHeader(
|
|
|
254 |
$response->headers,
|
|
|
255 |
array('content-type'));
|
|
|
256 |
}
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
$xrds = Services_Yadis_XRDS::parseXRDS($body, $extra_ns_map);
|
|
|
260 |
|
|
|
261 |
if ($xrds !== null) {
|
|
|
262 |
$y = new Services_Yadis_Yadis();
|
|
|
263 |
|
|
|
264 |
$y->request_uri = $request_uri;
|
|
|
265 |
$y->xrds = $xrds;
|
|
|
266 |
$y->uri = $uri;
|
|
|
267 |
$y->xrds_uri = $xrds_uri;
|
|
|
268 |
$y->body = $body;
|
|
|
269 |
$y->content_type = $content_type;
|
|
|
270 |
|
|
|
271 |
return $y;
|
|
|
272 |
} else {
|
|
|
273 |
return null;
|
|
|
274 |
}
|
|
|
275 |
}
|
|
|
276 |
|
|
|
277 |
/**
|
|
|
278 |
* Instantiates an empty Services_Yadis_Yadis object. This
|
|
|
279 |
* constructor should not be used by any user of the library.
|
|
|
280 |
* This constructor results in a completely useless object which
|
|
|
281 |
* must be populated with valid discovery information. Instead of
|
|
|
282 |
* using this constructor, call
|
|
|
283 |
* Services_Yadis_Yadis::discover($uri).
|
|
|
284 |
*/
|
|
|
285 |
function Services_Yadis_Yadis()
|
|
|
286 |
{
|
|
|
287 |
$this->request_uri = null;
|
|
|
288 |
$this->uri = null;
|
|
|
289 |
$this->xrds = null;
|
|
|
290 |
$this->xrds_uri = null;
|
|
|
291 |
$this->body = null;
|
|
|
292 |
$this->content_type = null;
|
|
|
293 |
}
|
|
|
294 |
|
|
|
295 |
/**
|
|
|
296 |
* Returns the list of service objects as described by the XRDS
|
|
|
297 |
* document, if this yadis object represents a successful Yadis
|
|
|
298 |
* discovery.
|
|
|
299 |
*
|
|
|
300 |
* @return array $services An array of {@link Services_Yadis_Service}
|
|
|
301 |
* objects
|
|
|
302 |
*/
|
|
|
303 |
function services()
|
|
|
304 |
{
|
|
|
305 |
if ($this->xrds) {
|
|
|
306 |
return $this->xrds->services();
|
|
|
307 |
}
|
|
|
308 |
|
|
|
309 |
return null;
|
|
|
310 |
}
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
?>
|