Tải bản đầy đủ (.pdf) (10 trang)

Thiết kế mạng xã hội với PHP - 39 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.65 MB, 10 trang )

Developing an API
[ 362 ]
$registry->getObject('template')->getPage()->addTag( 'siteurl',
$registry->getSetting('siteurl') );
$registry->getObject('template')-
>buildFromTemplates('header.tpl.php', 'main.tpl.php',
'footer.tpl.php');
$controllers = array();
$controllersSQL = "SELECT * FROM controllers WHERE active=1";
$registry->getObject('db')->executeQuery( $controllersSQL );
Next, we need to change the code that gets active controllers from the database.
Previously, it set the $controller variable for temporary use. This wasn't a problem
initially, because we reset the variable to the active controller after this. However,
now it is overriding our default controller. This is simply altered by changing
$controller to $cttrlr.
while( $cttrlr = $registry->getObject('db')->getRows() )
{
$controllers[] = $cttrlr['controller'];
}
The nal change is to only add authentication-related template bits to the view,
if the active controller isn't API.
if( $registry->getObject('authenticate')->isLoggedIn() && $controller
!= 'api')
{
$registry->getObject('template')->addTemplateBit('userbar',
'userbar_loggedin.tpl.php');
$registry->getObject('template')->getPage()->addTag( 'username',
$registry->getObject('authenticate')->getUser()->getUsername() );
}
elseif( $controller != 'api' )
{


$registry->getObject('template')->addTemplateBit('userbar',
'userbar.tpl.php');
}
if( in_array( $controller, $controllers ) )
{
require_once( FRAMEWORK_PATH . 'controllers/' . $controller .
'/controller.php');
$controllerInc = $controller.'controller';
$controller = new $controllerInc( $registry, true );
}
else
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 363 ]
{
// default controller, or pass control to CMS type system?
}
$registry->getObject('template')->parseOutput();
print $registry->getObject('template')->getPage()-
>getContentToPrint();
?>
Delegating control: API controllers for our
features
With the basic API controller in place, we can now add delegate controllers for the
features on our site. As discussed earlier, in this chapter we will only look at adding
support for proles; however, it is easy to extend should you wish to.
Prole's delegate
Our delegate controller simply needs to store the registry and caller objects, and then
depending on the nature of the user's request, either output a list of data, output the
data from one instance of a model, update a record via a model, or delete a record

via a model.
In this instance, we can't process create or delete requests, as creating a prole is
done on signup, and this feature requires a logged-in user. Deleting a user has lots
of implications, and shouldn't be done easily—it should be something the user can
do via the site itself, after a number of conrmations.
<?php
/**
* API Delegate: Profiles
* Proof of concept
*/
class APIDelegate{

private $registry;
private $caller;
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 364 ]
The constructor sets the registry and caller objects if a prole ID has been passed,
and then it calls the aProfile method. If no prole ID has been passed, it calls the
listProfiles method.
public function __construct( Registry $registry, $caller )
{
$this->caller = $caller;
$this->registry = $registry;
$urlBits = $this->registry->getObject('url')->getURLBits();
if( isset( $urlBits[2] ) )
{
$this->aProfile( intval( $urlBits[2] ) );
}
else

{
$this->listProfiles();
}
}

The listProfiles method rst calls the APIController's requireAuthentication
method. If authentication fails, that method will exit, preventing the rest of the
method from being executed. Since we can't create a prole, we should prohibit
submission of POST data. If the request is valid (and there isn't any POST data), then
we can query the proles table, convert it to JSON, and display it for the consumer.
This method should either be optimized to allow pagination or ltering (based on
searching for a user's name) or just to show members a user has a connection with
(otherwise it could return a lot of data).
private function listProfiles()
{
$this->caller->requireAuthentication();
if( $_SERVER['REQUEST_METHOD'] == 'POST' )
{
// we can't create a profile as we already have one!
header('HTTP/1.0 405 Method Not Allowed');
exit();
}
else
{
// ideally, we would paginate this, and/or put some filtering
in i.e. filter by name starting with A,B,C, etc.
$sql = "SELECT user_id, name FROM profile";
$this->registry->getObject('db')->executeQuery( $sql );
$r = array();
Download from Wow! eBook <www.wowebook.com>

Chapter 11
[ 365 ]
while( $row = $this->registry->getObject('db')->getRows() )
{
$r[] = $row;
}
header('HTTP/1.0 200 OK');
echo json_encode( $r );
exit();
}
}
If the URL dictates that the consumer is doing something with a specic user prole,
then the
aProfile method is called. As with the listProfiles method, it rst
requires authentication, and then includes the prole model path.
private function aProfile( $pid )
{
$this->caller->requireAuthentication();
require_once( FRAMEWORK_PATH . 'models/profile.php' );
if( $_SERVER['REQUEST_METHOD'] == 'PUT' )
{
If the request method is PUT, it assumes the consumer is trying to update the prole.
It veries the logged-in user owns the prole, and if they don't the appropriate
HTTP response code is issued. If they do own the prole, the validity of the prole
is checked, and then the prole is updated based on the PUT data.
if( $pid == $this->registry->getObject('authenticate')-
>getUser()->getUserID() )
{
$profile = new Profile( $this->registry, $pid );
if( $profile->isValid() )

{
$data = $this->caller->getRequestData();
$profile->setName( $this->registry->getObject('db')-
>sanitizeData( $data['name'] ) );
$profile->setDinoName( $this->registry->getObject('db')-
>sanitizeData( $data['dino_name'] ) );
// etc, set all appropriate methods
$profile->save();
header('HTTP/1.0 204 No Content');
exit();
}
else
{
header('HTTP/1.0 404 Not Found');
exit();
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 366 ]
}
}
else
{
header('HTTP/1.0 403 Forbidden');
exit();
}
}
else
{
If the request method isn't PUT, then it simply checks that the prole is valid, and
returns the prole data to the consumer. Depending on privacy settings, and the

relationship between the logged-in user and the user prole, we may want to
restrict the data that is presented.
$profile = new Profile( $this->registry, $pid );
if( $profile->isValid() )
{
header('HTTP/1.0 200 OK');
echo json_encode( $profile->toArray() );
exit();
}
else
{
header('HTTP/1.0 404 Not Found');
exit();
}
}
}
}
?>
Tweaking the proles model: validity and data
One thing our prole model doesn't do at the moment is provide any indication
if a particular prole was found within the database. This can be changed with a
new variable, a change to the constructor, and a getter method to return if it is
valid or not.
It also doesn't have a simple method for returning all of the properties in an array,
which can return to the consumer from our API.
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 367 ]
Revised controller
The additions to the model are highlighted in the code below:

/**
* Profile constructor
* @param Registry $registry the registry
* @param int $id the profile ID
* @return void
*/
public function __construct( Registry $registry, $id=0 )
{
$this->registry = $registry;
if( $id != 0 )
{
$this->id = $id;
// if an ID is passed, populate based off that
$sql = "SELECT * FROM profile WHERE user_id=" . $this->id;
$this->registry->getObject('db')->executeQuery( $sql );
if( $this->registry->getObject('db')->numRows() == 1 )
{
$this->valid = true;
$data = $this->registry->getObject('db')->getRows();
// populate our fields
foreach( $data as $key => $value )
{
$this->$key = $value;
}
}
else
{
$this->valid = false;
}
}

else
{
$this->valid = false;
}
}
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 368 ]
New getter: isValid()
We use a simple getter method to return the value of the valid variable.
/**
* Is the profile valid
* @return bool
*/
public function isValid()
{
return $this->valid;
}
New getter: toArray()
This is almost a copy of the toTags() method:
/**
* Return the users data
* @return array
*/
public function toArray( $prefix='' )
{
$r = array();
foreach( $this as $field => $data )
{
if( ! is_object( $data ) && ! is_array( $data ) )

{
$r[ $field ] = $data;
}
}
return $r;
}
Depending on privacy settings, we may want to lter the
information that is returned to the consumer, depending on
their status.
An Application Framework API
The API we have developed allows other websites and web services to interact with
our social network; it doesn't allow any provisions for our users to interact with
third-party applications within our site. Let's briey discuss what would be involved
in creating such an API, and the practical implications it presents.
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 369 ]
To allow third-party developers to build functionality that runs on our social
network, we would either need to provide a mechanism for them to upload code
to our servers (which is a big security risk, and should never be done), or a way for
them to host their code externally, but for our site to interact with it, communicating
login information between the two systems, taking the output generated and
rendering it through the Dino Space social network.
Even by having the code hosted externally, there are still security implications:
we would have to ensure no sensitive data (such as passwords) was passed to the
application. This would require an alternative method of authentication within the
API, either one using API keys or something like the OAuth standard. The developer
could also add in malicious HTML or JavaScript, such as code to trigger download
of a virus on the user's computer, something to trick the user into entering their
password, or any number of other things.

Social networks like Facebook get around this problem by allowing two methods
of integration:
• iFrame: The application is embedded through an iFrame. However,
the use of JavaScript is restricted.
• Alternatively, the code is written to generate special markup, which
Facebook then parses (ensuring it is clean).
The other problem with providing such a system is that developers would only use
it when the site starts to become popular. Until the site has proven itself developing
applications for it will be seen as a waste of developer's time, as there is no guarantee
that the site would be successful.
One solution: use OpenSocial
One potential solution is to use the OpenSocial API we discussed earlier. Because the
API is standard across all sites which use it, developers only need to develop their
application once, allowing it to be installed and used on any website that makes use
of the API. The API also provides a common way to authenticate and access data.
Consuming
As we have learned from this chapter, creating an API is a very large topic, one
which is covered in greater detail in a number of books dedicated to the subject.
Let's look at how we can quickly consume our new API using cURL
( />Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 370 ]
With cURL, we can pass our username and password, and it will handle passing the
appropriate values to authenticate against the basic authentication we have in place.
<?php
First, we need to set our username, password, and the URL we wish to connect to.
$username = 'michael';
$password = 'password';
$url = "http://localhost/api/users";
Next, we initialize a connection to the URL.

$ch = curl_init($url);
We then set a number of options, including the username and password, and if we
wish to have the headers returned to us.
curl_setopt($ch, CURLOPT_USERPWD, $username.':'.$password);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_NOBODY, 0);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array());
We then execute the cURL request and assign the data returned to a variable.
$response = curl_exec($ch);
We then store any additional information and close the cURL connection.
$responseInfo=curl_getinfo($ch);
curl_close($ch);
?>
In the code above, we have chosen to store the returned headers. For us to be able to
process the data returned from the API, we would need to strip out the headers and
process them accordingly. Since we use mod_rewrite to make search engine-friendly
URLs, we have a redirect header set before the API's header. So we would want to
ignore the rst header, and then process the second header. Everything after this
would be the response data to the request.
If we just want to work with the data returned, we can change the
CURLOPT_HEADER
option to 0.
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 371 ]
POSTing data to our API with cURL
To send POST data to our API using cURL, we simply build an array of the POST

data we wish to submit, convert the array into a suitable string, and then pass
these variables to our cURL request, as illustrated by the following code.
First, we set our
POST elds:
$fields = array( 'field' => urlencode( 'some data' );
$fields_string = '';
foreach( $fields as $key => $value )
{
$fields_string .= $key.'='.$value.'&';
}
rtrim($fields_string,'&');
We then pass the POST elds to our cURL request:
curl_setopt($ch,CURLOPT_POST,count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
Summary
In this chapter we have looked into the APIs that other social networks offer, and
discussed the advantages in providing APIs to our users. We then discussed the
various types of APIs available, before settling on REST as an API architecture
and developing our API in a RESTful way. Finally, we discussed the implications
involved in creating a third-party application API and how OpenSocial, an API we
discussed earlier in the chapter, could be used to integrate third-party applications.
Download from Wow! eBook <www.wowebook.com>

×