313 lines
10 KiB
PHP
313 lines
10 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* The core PHP Yadis implementation.
|
||
|
*
|
||
|
* PHP versions 4 and 5
|
||
|
*
|
||
|
* LICENSE: See the COPYING file included in this distribution.
|
||
|
*
|
||
|
* @package Yadis
|
||
|
* @author JanRain, Inc. <openid@janrain.com>
|
||
|
* @copyright 2005 Janrain, Inc.
|
||
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Need both fetcher types so we can use the right one based on the
|
||
|
* presence or absence of CURL.
|
||
|
*/
|
||
|
require_once "Services/Yadis/PlainHTTPFetcher.php";
|
||
|
require_once "Services/Yadis/ParanoidHTTPFetcher.php";
|
||
|
|
||
|
/**
|
||
|
* Need this for parsing HTML (looking for META tags).
|
||
|
*/
|
||
|
require_once "Services/Yadis/ParseHTML.php";
|
||
|
|
||
|
/**
|
||
|
* Need this to parse the XRDS document during Yadis discovery.
|
||
|
*/
|
||
|
require_once "Services/Yadis/XRDS.php";
|
||
|
|
||
|
/**
|
||
|
* This is the core of the PHP Yadis library. This is the only class
|
||
|
* a user needs to use to perform Yadis discovery. This class
|
||
|
* performs the discovery AND stores the result of the discovery.
|
||
|
*
|
||
|
* First, require this library into your program source:
|
||
|
*
|
||
|
* <pre> require_once "Services/Yadis/Yadis.php";</pre>
|
||
|
*
|
||
|
* To perform Yadis discovery, first call the "discover" method
|
||
|
* statically with a URI parameter:
|
||
|
*
|
||
|
* <pre> $http_response = array();
|
||
|
* $fetcher = Services_Yadis_Yadis::getHTTPFetcher();
|
||
|
* $yadis_object = Services_Yadis_Yadis::discover($uri,
|
||
|
* $http_response, $fetcher);</pre>
|
||
|
*
|
||
|
* If the discovery succeeds, $yadis_object will be an instance of
|
||
|
* {@link Services_Yadis_Yadis}. If not, it will be null. The XRDS
|
||
|
* document found during discovery should have service descriptions,
|
||
|
* which can be accessed by calling
|
||
|
*
|
||
|
* <pre> $service_list = $yadis_object->services();</pre>
|
||
|
*
|
||
|
* which returns an array of objects which describe each service.
|
||
|
* These objects are instances of Services_Yadis_Service. Each object
|
||
|
* describes exactly one whole Service element, complete with all of
|
||
|
* its Types and URIs (no expansion is performed). The common use
|
||
|
* case for using the service objects returned by services() is to
|
||
|
* write one or more filter functions and pass those to services():
|
||
|
*
|
||
|
* <pre> $service_list = $yadis_object->services(
|
||
|
* array("filterByURI",
|
||
|
* "filterByExtension"));</pre>
|
||
|
*
|
||
|
* The filter functions (whose names appear in the array passed to
|
||
|
* services()) take the following form:
|
||
|
*
|
||
|
* <pre> function myFilter(&$service) {
|
||
|
* // Query $service object here. Return true if the service
|
||
|
* // matches your query; false if not.
|
||
|
* }</pre>
|
||
|
*
|
||
|
* This is an example of a filter which uses a regular expression to
|
||
|
* match the content of URI tags (note that the Services_Yadis_Service
|
||
|
* class provides a getURIs() method which you should use instead of
|
||
|
* this contrived example):
|
||
|
*
|
||
|
* <pre>
|
||
|
* function URIMatcher(&$service) {
|
||
|
* foreach ($service->getElements('xrd:URI') as $uri) {
|
||
|
* if (preg_match("/some_pattern/",
|
||
|
* $service->parser->content($uri))) {
|
||
|
* return true;
|
||
|
* }
|
||
|
* }
|
||
|
* return false;
|
||
|
* }</pre>
|
||
|
*
|
||
|
* The filter functions you pass will be called for each service
|
||
|
* object to determine which ones match the criteria your filters
|
||
|
* specify. The default behavior is that if a given service object
|
||
|
* matches ANY of the filters specified in the services() call, it
|
||
|
* will be returned. You can specify that a given service object will
|
||
|
* be returned ONLY if it matches ALL specified filters by changing
|
||
|
* the match mode of services():
|
||
|
*
|
||
|
* <pre> $yadis_object->services(array("filter1", "filter2"),
|
||
|
* SERVICES_YADIS_MATCH_ALL);</pre>
|
||
|
*
|
||
|
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link
|
||
|
* SERVICES_YADIS_MATCH_ANY}.
|
||
|
*
|
||
|
* Services described in an XRDS should have a library which you'll
|
||
|
* probably be using. Those libraries are responsible for defining
|
||
|
* filters that can be used with the "services()" call. If you need
|
||
|
* to write your own filter, see the documentation for {@link
|
||
|
* Services_Yadis_Service}.
|
||
|
*
|
||
|
* @package Yadis
|
||
|
*/
|
||
|
class Services_Yadis_Yadis {
|
||
|
|
||
|
/**
|
||
|
* Returns an HTTP fetcher object. If the CURL extension is
|
||
|
* present, an instance of {@link Services_Yadis_ParanoidHTTPFetcher}
|
||
|
* is returned. If not, an instance of
|
||
|
* {@link Services_Yadis_PlainHTTPFetcher} is returned.
|
||
|
*/
|
||
|
function getHTTPFetcher($timeout = 20)
|
||
|
{
|
||
|
if (Services_Yadis_Yadis::curlPresent()) {
|
||
|
$fetcher = new Services_Yadis_ParanoidHTTPFetcher($timeout);
|
||
|
} else {
|
||
|
$fetcher = new Services_Yadis_PlainHTTPFetcher($timeout);
|
||
|
}
|
||
|
return $fetcher;
|
||
|
}
|
||
|
|
||
|
function curlPresent()
|
||
|
{
|
||
|
return function_exists('curl_init');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @access private
|
||
|
*/
|
||
|
function _getHeader($header_list, $names)
|
||
|
{
|
||
|
foreach ($header_list as $name => $value) {
|
||
|
foreach ($names as $n) {
|
||
|
if (strtolower($name) == strtolower($n)) {
|
||
|
return $value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @access private
|
||
|
*/
|
||
|
function _getContentType($content_type_header)
|
||
|
{
|
||
|
if ($content_type_header) {
|
||
|
$parts = explode(";", $content_type_header);
|
||
|
return strtolower($parts[0]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This should be called statically and will build a Yadis
|
||
|
* instance if the discovery process succeeds. This implements
|
||
|
* Yadis discovery as specified in the Yadis specification.
|
||
|
*
|
||
|
* @param string $uri The URI on which to perform Yadis discovery.
|
||
|
*
|
||
|
* @param array $http_response An array reference where the HTTP
|
||
|
* response object will be stored (see {@link
|
||
|
* Services_Yadis_HTTPResponse}.
|
||
|
*
|
||
|
* @param Services_Yadis_HTTPFetcher $fetcher An instance of a
|
||
|
* Services_Yadis_HTTPFetcher subclass.
|
||
|
*
|
||
|
* @param array $extra_ns_map An array which maps namespace names
|
||
|
* to namespace URIs to be used when parsing the Yadis XRDS
|
||
|
* document.
|
||
|
*
|
||
|
* @param integer $timeout An optional fetcher timeout, in seconds.
|
||
|
*
|
||
|
* @return mixed $obj Either null or an instance of
|
||
|
* Services_Yadis_Yadis, depending on whether the discovery
|
||
|
* succeeded.
|
||
|
*/
|
||
|
function discover($uri, &$http_response, &$fetcher,
|
||
|
$extra_ns_map = null, $timeout = 20)
|
||
|
{
|
||
|
if (!$uri) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
$request_uri = $uri;
|
||
|
$headers = array("Accept: application/xrds+xml");
|
||
|
|
||
|
if (!$fetcher) {
|
||
|
$fetcher = Services_Yadis_Yadis::getHTTPFetcher($timeout);
|
||
|
}
|
||
|
|
||
|
$response = $fetcher->get($uri, $headers);
|
||
|
$http_response = $response;
|
||
|
|
||
|
if (!$response) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if ($response->status != 200) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
$xrds_uri = $response->final_url;
|
||
|
$uri = $response->final_url;
|
||
|
$body = $response->body;
|
||
|
|
||
|
$xrds_header_uri = Services_Yadis_Yadis::_getHeader(
|
||
|
$response->headers,
|
||
|
array('x-xrds-location',
|
||
|
'x-yadis-location'));
|
||
|
|
||
|
$content_type = Services_Yadis_Yadis::_getHeader($response->headers,
|
||
|
array('content-type'));
|
||
|
|
||
|
if ($xrds_header_uri) {
|
||
|
$xrds_uri = $xrds_header_uri;
|
||
|
$response = $fetcher->get($xrds_uri);
|
||
|
$http_response = $response;
|
||
|
if (!$response) {
|
||
|
return null;
|
||
|
} else {
|
||
|
$body = $response->body;
|
||
|
$headers = $response->headers;
|
||
|
$content_type = Services_Yadis_Yadis::_getHeader($headers,
|
||
|
array('content-type'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Services_Yadis_Yadis::_getContentType($content_type) !=
|
||
|
'application/xrds+xml') {
|
||
|
// Treat the body as HTML and look for a META tag.
|
||
|
$parser = new Services_Yadis_ParseHTML();
|
||
|
$new_uri = $parser->getHTTPEquiv($body);
|
||
|
$xrds_uri = null;
|
||
|
if ($new_uri) {
|
||
|
$response = $fetcher->get($new_uri);
|
||
|
if ($response->status != 200) {
|
||
|
return null;
|
||
|
}
|
||
|
$http_response = $response;
|
||
|
$body = $response->body;
|
||
|
$xrds_uri = $new_uri;
|
||
|
$content_type = Services_Yadis_Yadis::_getHeader(
|
||
|
$response->headers,
|
||
|
array('content-type'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$xrds = Services_Yadis_XRDS::parseXRDS($body, $extra_ns_map);
|
||
|
|
||
|
if ($xrds !== null) {
|
||
|
$y = new Services_Yadis_Yadis();
|
||
|
|
||
|
$y->request_uri = $request_uri;
|
||
|
$y->xrds = $xrds;
|
||
|
$y->uri = $uri;
|
||
|
$y->xrds_uri = $xrds_uri;
|
||
|
$y->body = $body;
|
||
|
$y->content_type = $content_type;
|
||
|
|
||
|
return $y;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Instantiates an empty Services_Yadis_Yadis object. This
|
||
|
* constructor should not be used by any user of the library.
|
||
|
* This constructor results in a completely useless object which
|
||
|
* must be populated with valid discovery information. Instead of
|
||
|
* using this constructor, call
|
||
|
* Services_Yadis_Yadis::discover($uri).
|
||
|
*/
|
||
|
function Services_Yadis_Yadis()
|
||
|
{
|
||
|
$this->request_uri = null;
|
||
|
$this->uri = null;
|
||
|
$this->xrds = null;
|
||
|
$this->xrds_uri = null;
|
||
|
$this->body = null;
|
||
|
$this->content_type = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the list of service objects as described by the XRDS
|
||
|
* document, if this yadis object represents a successful Yadis
|
||
|
* discovery.
|
||
|
*
|
||
|
* @return array $services An array of {@link Services_Yadis_Service}
|
||
|
* objects
|
||
|
*/
|
||
|
function services()
|
||
|
{
|
||
|
if ($this->xrds) {
|
||
|
return $this->xrds->services();
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
?>
|