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

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

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.75 MB, 10 trang )

Developing an API
[ 352 ]
Let's go with REST
REST is now a very popular API architecture, with most social networks providing
REST-based APIs. The way RESTful APIs rely on descriptive URIs for providing access
to data makes them very easy for consumers to utilize, as the URIs provide information
on what the request does, and what data it will return. Some implementations, such
as the Twitter API even make it possible to change the format of the data returned,
simply by changing a part of the URI.
Requests
Requests to a RESTful API use HTTP verbs to describe what the consumer is trying
to do. The API requests are made to specic URIs, which dene the resource that the
consumer is trying to perform the action (determined by the verbs) upon.
HTTP verbs
The HTTP verbs and their usage are described as follows:
Verb Description
GET
Retrieve information
POST
Create records
PUT
Update records
DELETE
Delete records
Resources
RESTful APIs relate URIs to resources. Below are some examples:
•
To list or create proles
•
A specic user's prole
Our RESTful API will be based within an API controller, thus


prexing all URLs with api/, which goes slightly against the REST
concept of a resource.
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 353 ]
Resources and verbs—the requests
Let's look at how resources and verbs combined result in API requests.
API operation HTTP verb Resource
Creating a user
POST />Listing users
GET />Viewing a user's prole
GET />Updating a prole
PUT />Deleting a prole
DELETE />In the above resources, the number 1 represents the
ID of a user's prole.
Responses
The response to an API request is generally made up of two parts. The rst part
of the response is the HTTP header containing an appropriate status code. Some
examples of HTTP status codes are below:
HTTP status code Meaning
200 OK
201 Created
400 Bad request
404 Not found
Within PHP, HTTP status codes are set as follows:
header("HTTP/1.0404NotFound");
The second part of the response is the data itself; for instance, if the API request was
for a list of users, the response would be the list of users. Commonly, response data
is sent as XML or JSON. Some APIs allow the consumer to request the format of the
response by supplying the format to the API. We are going to use JSON. If we have

an array of data that we want to return as JSON, we simply do the following:
echo json_encode( $users_list_array );
exit();
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 354 ]
Further reading
There are numerous resources available regarding web services and REST.
Following are the resources you may nd particularly useful.
RESTful PHP Web Services
Packt has a book dedicated to creating RESTful APIs in PHP—RESTful PHP Web
Services, by Samisa Abeysinghe, />services/book.
This book details the concepts of a REST architecture, how
to make use of existing RESTful APIs in your framework, how to create a RESTful
API for other applications to interact with, as well as debugging information and
case studies.
Conference talks
Lorna Jane Mitchell (), a widely-respected developer
and conference speaker on PHP-related topics, has recently spoken at a number
of conferences on the subject of web service design. Slides from related talks are
available online: />web-service-design
, />services-perfect-partners.
Implementation
Now that we know what sort of API we are going to develop, we can move onto the
implementation. In this chapter we will only implement a small sub-set of the API's
functionality. Feel free to extend this to match the entire functionality of Dino Space,
if you wish.
Data format
Most commonly, RESTful APIs either return their data in XML format or as JSON.
Some APIs allow the consumer to specify the return type by adding .xml or .json

to the end of the URL. For the purposes of our implementation, let's stick to JSON,
as it is simpler to convert data to JSON (simply by passing the data to the
json_encode function).
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 355 ]
API controller
Our API controller itself won't do very much; instead it will pass control to
delegate controllers, which contain logic specic to the various sections of
the site.
<?php
/**
* API Controller
*/
class Apicontroller{
To indicate which les are available for control to be delegated to, we should
maintain an array of allowable API controllers. For our work in this chapter,
we will create the profiles delegate.
/**
* Allowable API Controllers, for control to be delegated to
*/
private $allowableAPIControllers = array( 'profiles' );

/**
* Request data
*/
private $requestData = array();
The object's constructor simply sets the registry object, gets the value of the API
delegate that should be used, and calls the delegator method (delegateControl).
/**

* API Controller Constructor
* @param Registry $registry the registry
* @param boolean $directCall
* @return void
*/
public function __construct( Registry $registry, $directCall=true )
{
$this->registry = $registry;
$apiController = $registry->getObject('url')->getURLBit(1);
$this->delegateControl( $apiController );
}
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 356 ]
The delegateControl method checks that the delegate controller is within the
allowed delegates. If it is, then it includes the appropriate controller, instantiates
it, and passes the registry and the API controller object to it. There are a number of
methods that will be common to all API delegates. These methods are stored in this
object, and called by the delegate referencing this object. If the requested controller
is not allowable, then we generate an appropriate HTTP status code; in this case:
404 Not Found.
/**
* Pass control to a delegate
* @param String $apiController the delegate
* @return void
*/
private function delegateControl( $apiController )
{
if( $apiController != '' && in_array( $apiController,
$this->allowableAPIControllers ) )

{
require_once( FRAMEWORK_PATH . 'controllers/api/' .
$apiController . '.php' );
$api = new APIDelegate( $this->registry, $this );
}
else
{
header('HTTP/1.0 404 Not Found');
exit();
}
}
A shared method is required by our delegates. This is called if a delegate requires the
API user to be an authenticated user on the site. It generates a basic authentication
prompt (this is presented to users viewing the site in their browsers, but for API
users the username and password are passed as part of the HTTP request).
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 357 ]
Alternatives to basic authentication
Basic authentication isn't the best option in terms of security, especially
if many websites begin offering services utilizing our API. Our users'
passwords could be stored (with their permission) within these websites,
putting reliance on the integrity and security of those sites and their
owners. An alternative is OAuth, where the API provider deals with the
authentication, and provides consumers with an API key for their users. If
a user then wishes to stop a third-party service utilizing their account via
the API, they can simply revoke access. We will discuss this option more
in the security section of this chapter.
If the authentication fails, then the 401 Unauthorized status code is issued.
/**

* Request authentication for access to API methods, called by
delegates
* @return void
*/
public function requireAuthentication()
{
if( !isset( $_SERVER['PHP_AUTH_USER'] ) )
{
header('WWW-Authenticate: Basic realm="DinoSpace API Login"');
header('HTTP/1.0 401 Unauthorized');
exit();
}
else
{
$user = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
$this->registry->getObject('authenticate')->postAuthenticate(
$user, $password, false );
if( ! $this->registry->getObject('authenticate')-
>isLoggedIn() )
{
header('HTTP/1.0 401 Unauthorized');
exit();
}
}
}
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 358 ]
PUT and DELETE data (technically, there should never be DELETE data sent on a

DELETE request) cannot be accessed through super globals as POST and GET data
can ($_POST and $_GET), so we need a mechanism to get the request data, regardless
of the type of request.
/**
* Get the type of request
* @return array
*/
public function getRequestData()
{
if( $_SERVER['REQUEST_METHOD'] == 'GET' )
{
$this->requestData = $_GET;
}
elseif( $_SERVER['REQUEST_METHOD'] == 'POST' )
{
$this->requestData = $_POST;
}
elseif( $_SERVER['REQUEST_METHOD'] == 'PUT' )
{
parse_str(file_get_contents('php://input'),
$this->requestData );
}
elseif( $_SERVER['REQUEST_METHOD'] == 'DELETE' )
{
parse_str(file_get_contents('php://input'),
$this->requestData );
}
return $this->requestData;
}
}

?>
php://input
php://input is an input stream wrapper in PHP, which allows us to
read raw request data. More detailed information is available on the PHP
website: />Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 359 ]
Wait—no models?
That's right; we don't need to create any models for our API. All of the functionality
our API needs to provide already exists through the various models we have created.
So instead of creating API-specic models, we will create some additional API
controllers, which work with the pre-existing models to get the data and present
it to the consumer.
Authentication
Keeping with the RESTful way of leveraging HTTP, we can make use of HTTP
authentication to authenticate the user. This is where authentication details are
passed as part of the HTTP request from our API consumer. You will have seen
examples of this if you have ever visited a web page, and your browser has opened
a pop up prompting for authentication details before loading the page. In this case,
your browser reads the server's request for authentication, and then requests login
details before sending the authentication request to the server.
More information
You can read more about HTTP authentication with PHP here:
/>Sessions lead to unREST!
REST is a stateless architecture, which means all of the information required for a
particular operation or request should be included within that request. It shouldn't
rely on information from a previous request or other information such as sessions
and cookies. To that end, we should amend our authenticate registry object and
our index.php le.
Amending the authenticate registry object

We need to amend the authenticate registry class (registry/authenticate.
class.php
) to only set $_SESSION data if that is required, so that we can indicate,
from our API controller, that we don't want $_SESSION data to be created.
Download from Wow! eBook <www.wowebook.com>
Developing an API
[ 360 ]
We should add an optional parameter to the postAuthenticate method to indicate
if $_SESSION data should be set, with a default value of true so it doesn't impact on
other aspects of our site, which we have already implemented.
public function postAuthenticate( $u, $p, $sessions=true )
{
$this->justProcessed = true;
require_once(FRAMEWORK_PATH.'registry/user.class.php');
$this->user = new User( $this->registry, 0, $u, $p );
if( $this->user->isValid() )
{
if( $this->user->isActive() == false )
{
$this->loggedIn = false;
$this->loginFailureReason = 'inactive';
}
elseif( $this->user->isBanned() == true )
{
$this->loggedIn = false;
$this->loginFailureReason = 'banned';
}
else
{
$this->loggedIn = true;

If the sessions parameter for this method has been set to true, then we set the
appropriate session. If it has been set to false (for example, by our API controller),
then it is not set.
if( $sessions == true )
{
$_SESSION['sn_auth_session_uid'] = $this->user->getUserID();
}
}
}
else
{
$this->loggedIn = false;
$this->loginFailureReason = 'invalidcredentials';
}
}
Download from Wow! eBook <www.wowebook.com>
Chapter 11
[ 361 ]
Amending index.php
Our index.php le by default checks for SESSION data for authentication.
See Chapter 2, or take a look at the index.php le to
refresh your memory.
<?php
session_start();
DEFINE("FRAMEWORK_PATH", dirname( __FILE__ ) ."/" );
require('registry/registry.class.php');
$registry = new Registry();
// setup our core registry objects
$registry->createAndStoreObject( 'template', 'template' );
$registry->createAndStoreObject( 'mysqldb', 'db' );

$registry->createAndStoreObject( 'authenticate', 'authenticate' );
$registry->createAndStoreObject( 'urlprocessor', 'url' );
$registry->getObject('url')->getURLData();
// database settings
include(FRAMEWORK_PATH . 'config.php');
// create a database connection
$registry->getObject('db')->newConnection( $configs['db_host_sn'],
$configs['db_user_sn'], $configs['db_pass_sn'],
$configs['db_name_sn']);
Firstly, we need to move the line that sets the controller variable to just before
authentication is checked. We then wrap the authentication check line in an IF
statement, so that it is only executed if the controller being requested isn't the
API controller.
$controller = $registry->getObject('url')->getURLBit(0);
if( $controller != 'api' )
{
$registry->getObject('authenticate')->checkForAuthentication();
}
// store settings in our registry
$settingsSQL = "SELECT `key`, `value` FROM settings";
$registry->getObject('db')->executeQuery( $settingsSQL );
while( $setting = $registry->getObject('db')->getRows() )
{
$registry->storeSetting( $setting['value'], $setting['key'] );
}
Download from Wow! eBook <www.wowebook.com>

×