PHP — php精粹-编写高效的php代码 --- API

1.数据格式

(1)json

示例代码:

$jsonData = '[{"title":"The Magic Flute","time":1329636600}, {"title":"Vivaldi Four Seasons","time":1329291000},{"title":"Mozart\'s Requiem","time":1330196400}]';

$concerts = json_decode($jsonData, true);
print_r($concerts);

/*
Output:
Array
(
    [0] => Array
        (
            [title] => The Magic Flute
            [time] => 1329636600
        )

    [1] => Array
        (
            [title] => Vivaldi Four Seasons
            [time] => 1329291000
        )

    [2] => Array
        (
            [title] => Mozart's Requiem
            [time] => 1330196400
        )

)
*/

(2)xml

示例代码:

$simplexml = new SimpleXMLElement(
  '<?xml version="1.0"?><concerts />');

$concert1 = $simplexml->addChild('concert');
$concert1->addChild("title", "The Magic Flute");
$concert1->addChild("time", 1329636600);

$concert2 = $simplexml->addChild('concert');
$concert2->addChild("title", "Vivaldi Four Seasons");
$concert2->addChild("time", 1329291000);

$concert3 = $simplexml->addChild('concert');
$concert3->addChild("title", "Mozart's Requiem");
$concert3->addChild("time", 1330196400);

echo $simplexml->asXML();

/* output:
<concerts><concert><title>The Magic Flute</title><time>1329636600</time></concert><concert>
<title>Vivaldi Four Seasons</title><time>1329291000</time></concert><concert><title>Mozart's Requiem</title>
<time>1330196400</time></concert></concerts>

2.HTTP协议

(1)发送HTTP请求

①cURL

用于请求URL的简单命令行工具

curl http://www.lys.com

②PHP cURL扩展

代码:

$ch = curl_init('http://api.bitly.com/v3/shorten'
  . '?login=user&apiKey=secret'
  . '&longUrl=http%3A%2F%2Fsitepoint.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);
print_r(json_decode($result));

/* output:
stdClass Object
(
    [status_code] => 200
    [status_txt] => OK
    [data] => stdClass Object
        (
            [long_url] => http://sitepoint.com/
            [url] => http://bit.ly/qmcGU2
            [hash] => qmcGU2
            [global_hash] => 3mWynL
            [new_hash] => 0
        )

)
*/

③使用pecl_http扩展

示例代码:

$request = new HttpRequest('http://api.bitly.com/v3/shorten'
  . '?login=user&apiKey=secret'
  . '&longUrl=http%3A%2F%2Fsitepoint.com');
$request->send();

$result = $request->getResponseBody();
print_r(json_decode($result));

/* output:
stdClass Object
(
    [status_code] => 200
    [status_txt] => OK
    [data] => stdClass Object
        (
            [long_url] => http://sitepoint.com/
            [url] => http://bit.ly/qmcGU2
            [hash] => qmcGU2
            [global_hash] => 3mWynL
            [new_hash] => 0
        )

)
*/

④PHP流

配置php.ini,启用allow_url_fopen选项

示例代码:

  $post_data['password'] = implode('', $password);
      $context = stream_context_create(array('http' => array(
        'method' => 'POST',
        'follow_location' => false,
        'header' => 'Content-Type: application/x-www-form-urlencoded',
        'content' => http_build_query($post_data)
      )));
      $response = file_get_contents($url, false, $context);
  

(2)HTTP状态码

PHP — php精粹-编写高效的php代码 --- API

(3)HTTP文件头

在php应用程序中返回文件头信息:

代码:

header(‘Content-Type:text/html;charset=utf8’);
header(‘HTTP/1.1 404 Not Found’);

PHP — php精粹-编写高效的php代码 --- API

3.理解并选择服务类型

(1)、一些术语

RPC 是Remote ProcedureCall(远程调用过程)的缩写。(XML-RPC JSON-RPC)

SOAP (Simple Object Access Protocol),我们通过使用WSDL(Web Service Description Language,Web服务描述语言),这是用来描述Web服务的一组定义。

REST REST将每个项目都视为一个资源,我们通过发送正确的动词为这个资源执行动作。

(2)、使用SOAP

①非SOAP模式

代码:

class ServiceFunctions
{
  public function getDisplayName($first_name, $last_name) {
    $name = '';
    $name .= strtoupper(substr($first_name, 0, 1));
    $name .= ' ' . ucfirst($last_name);
    return $name;
  }

  public function countWords($paragraph) {
    $words = preg_split('/[. ,!?;]+/',$paragraph);
    return count($words);
  }
}

//创建SOAP服务
include 'ServiceFunctions.php';
$options = array('uri'=>'http://localhost/');
$server = new SoapServer(NULL, $options);
$server->setClass('ServiceFunctions');
$server->handle();

//使用这个服务
$options = array(
        'uri'=>'http://localhost',
        'location'=>'http://localhost/php_lib/oop/spbooks/chapter_03/SoapServer.php',
        'trace'=>1
        );
$client = new SoapClient(NULL, $options);

echo $client->getDisplayName('luoyunshu','moushu');

//调试代码
$functions = $client->__getFunctions();
var_dump($functions);
$request = $client->__getLastRequest();
var_dump($request);
$headers = $client->__getLastRequestHeaders();
var_dump($headers);

②使用WSDL描述SOAP服务

可用phpclasses.org中带有的WSDL生成器生成WSDL

创建SOAP客户端

示例代码:

ini_set('soap.wsdl_cache_enabled',0);

$client = new SoapClient('http://localhost/php_lib/oop/spbooks/chapter_03/wsdl.xml');

    $functions = $client->__getFunctions();
    var_dump($functions);

(3)RPC服务

①建立RPC服务

示例代码:

class ServiceFunctions
{
  public function getDisplayName($first_name, $last_name) {
    $name = '';
    $name .= strtoupper(substr($first_name, 0, 1));
    $name .= ' ' . ucfirst($last_name);
    return $name;
  }

  public function countWords($paragraph) {
    $words = preg_split('/[. ,!?;]+/',$paragraph);
    return count($words);
  }
}

json_rpc.php:

require 'servicefunctions.php';

if(isset($_GET['method'])) {
  switch($_GET['method']) {
    case 'countWords':
      $response = ServiceFunctions::countWords($_GET['words']);
      break;
    case 'getDisplayName':
      $response = ServiceFunctions::getdisplayName($_GET['first_name'], $_GET['last_name']);
      break;
    default:
      $response = "Unknown Method";
      break;
  }
} else {
  $response = "Unknown Method";
}

header('Content-Type: application/json');
echo json_encode($response);

要使用API中的这些方法,我们仅仅需发送如下的URL的请求:

http://localhost/json_rpc.php?method=getDisplayName&first_name=ms&last_name=lys

==>一个好的API将会支持不同的输出和类似这样的结构, 甚至错误消息也通过相同的过程输出,我们可用不同方式灵活地对输出就行解码。

(4)开发和使用RESTful服务

①建立一个RESTful服务(mvc模式)

a.使用重写规则重定向到index.php

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php/$1 [L]

</IfModule>

b.收集传过来的数据

index.php:

/* vim: set shiftwidth=2:*/
function __autoload($classname) {
  require strtolower($classname) . '.php';
}

// initialise the request object and store the requested URL
$request = new Request();
$request->url_elements = array();
if(isset($_SERVER['PATH_INFO'])) {
  $request->url_elements = explode('/', $_SERVER['PATH_INFO']);
    //var_dump($request->url_elements);
    //exit();
}

// figure out the verb and grab the incoming data
$request->verb = $_SERVER['REQUEST_METHOD'];
//echo $request->verb;
switch($request->verb) {
  case 'GET':
    $request->parameters = $_GET;
    break;
  case 'POST':
  case 'PUT':
    $request->parameters = json_decode(file_get_contents('php://input'), 1);
    break;
  case 'DELETE':
  default:
    // we won't set any parameters in these cases
    $request->parameters = array();
}
//var_dump($request->url_elements);
//exit();
// route the request
if($request->url_elements) {
  $controller_name = ucfirst($request->url_elements[1]) . 'Controller';
  if(class_exists($controller_name)) {
    $controller = new $controller_name();
    $action_name = ucfirst($request->verb) . "Action";
    $response = $controller->$action_name($request);
  } else {
    header('HTTP/1.0 400 Bad Request');
    $response = "Unknown Request for " . $request->url_elements[1];
  }
} else {
  header('HTTP/1.0 400 Bad Request');
  $response = "Unknown Request";
}

echo json_encode($response);

c.新建一个控制器(eventscontroller.php)

/* vim: set shiftwidth=2:*/
class EventsController
{
  protected $events_file = 'events-list.txt';

  public function GETAction($request) {     //处理get请求
    $events = $this->readEvents();
    //var_dump($events);
    //exit();
    if(isset($request->url_elements[2]) && is_numeric($request->url_elements[2])) {
      if(isset($events[$request->url_elements[2]])) {
          return $events[$request->url_elements[2]];
      }else {
          return array();
      }
    } else {
      return $events;
    }
  }

  public function POSTAction($request) {     //处理post请求
    // error checking and filtering input MUST go here
    $events = $this->readEvents();
    $event = array();
    $event['title'] = $request->parameters['title'];
    $event['date'] = $request->parameters['date'];
    $event['capacity'] = $request->parameters['capacity'];

    $events[] = $event;
    $this->writeEvents($events);
    $id = max(array_keys($events));
    header('HTTP/1.1 201 Created');
    header('Location:/events/'. $id);
    return '';
  }

  public function PUTAction($request) {        //处理put请求
    // error checking and filtering input MUST go here
    $events = $this->readEvents();
    $event = array();
    $event['title'] = $request->parameters['title'];
    $event['date'] = $request->parameters['date'];
    $event['capacity'] = $request->parameters['capacity'];
    $id = $request->url_elements[2];
    $events[$id] = $event;
    $this->writeEvents($events);
    header('HTTP/1.1 204 No Content');
    header('Location: /events/'. $id);
    return '';
  }

  public function DELETEAction($request) {     //处理delete请求
    $events = $this->readEvents();
    if(isset($request->url_elements[2]) && is_numeric($request->url_elements[2])) {
      unset($events[$request->url_elements[2]]);
      $this->writeEvents($events);
      header('HTTP/1.1 204 No Content');
      header('Location: /events');
    }
    return '';
  }

  protected function readEvents() {
    $events = unserialize(file_get_contents($this->events_file));
    if(empty($events)) {
      // invent some event data
      $events[] = array('title' => 'Summer Concert',
        'date' => date('U', mktime(0,0,0,7,1,2012)),
        'capacity' => '150');
      $events[] = array('title' => 'Valentine Dinner',
        'date' => date('U', mktime(0,0,0,2,14,2012)),
        'capacity' => '48');
      $this->writeEvents($events);
    }
    return $events;
  }

  protected function writeEvents($events) {
    file_put_contents($this->events_file, serialize($events));
    return true;
  }
}

需要加载的Request类

request.php:

class Request { }

4.选择一个web服务

决定你将采用哪种服务形式,如果你的服务和表述数据结合很紧密,你可能会选择RESTful服务。对于计算机之间的数据交换,你可能选择XML-RPC或SOAP,特别是在确信SOAP已被人们透彻理解的企业环境下。当我们从JavaScript传输异步或者传输到移动设备时,JSON也许是一个更好的选择。

一个健壮性和可靠性的服务将对非破坏性的服务将对非破坏性的失败做出反应。而且把哪里发生错误的信息反馈给用户。错误信息应该以同样的格式返回,如同一个成功的输出将会到达。

有一个设计原则我们陈其为KISS(Keep it Simple,stupid 保持简单、无趣),就API设计而言少即是多。要当心避免创建一个广泛的、不规则的、不稳定的API。只有我们真正需要的时候才添加功能,并且要确保新功能和其他API实现的方式一致。

上一篇:mysql查询缓存


下一篇:程序员的十大经典口头禅你知道吗