一个超简单且轻量级的PHP库,使制作RESTful API更加容易。

1.3.0 2018-03-18 07:58 UTC

README

Latest Stable Version Total Downloads Latest Unstable Version License

一个轻量级、无依赖的库,使在PHP中编写基于文件的RESTful JSON API端点更加容易。

设置

将仓库克隆到您的项目中。假设您的RESTful端点位于 /api 目录中,我建议创建一个 /api/vendor 文件夹或只是一个普通的 /api/libs 文件夹,并将此仓库克隆到其中。

  • 在您的项目中,使用 require_once 包含到 Http_Autoloader.php 的路径。
<?php

require_once 'path/to/Http_Autoloader.php';

# ...or potentially...

require_once 'project_root/dist/api/vendor/Http_lib/Http_Autoloader.php';

实例化

<?php

$http = new Http;

属性

Http 的属性以JSON表示

{
  "request": "type: class Http\Request",
  "response": "type: class Http\Response",
  "get": "type: callable (callback handle)",
  "post": "type: callable (callback handle)",
  "put": "type: callable (callback handle)",
  "patch": "type: callable (callback handle)",
  "delete": "type: callable (callback handle)"
}

Http\Request 的某些示例属性以JSON表示(这些属性根据请求本身而有所不同)

{
  "body": {
    "content": "this is some test content from a json request"
  },
  "method": "POST",
  "requestURI": "/php/my_libs_tests/Http.test.php/1?stuff=some+stuff",
  "query": {
    "stuff": "some stuff"
  },
  "file": "Http.test.php",
  "contentType": "application/json",
  "cookies": "PHPSESSID=01kf9mqndmpr8guqe6tk87nka7; _ga=GA1.1.162483231.1471457216",
  "host": "localhost",
  "port": "80",
  "pathInfo": "/1",
  "scriptName": "/php/my_libs_tests/Http.test.php",
  "URIComponents": {
    "path": "/php/my_libs_tests/Http.test.php/1",
    "query": "stuff=some+stuff"
  },
  "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
}

Http\Reponse 的属性是私有的。这允许响应对象管理响应数据并在发送时序列化它。

方法

Http 类为每个主要的HTTP动词(get、post、put、patch、delete)都提供了一个方法。这允许您将回调附加到每个适当的请求方法。您可以传入您回调的字符串名称,或者作为闭包内联编写您的函数。回调将使用 Http 的实例来调用。您可以将字符串名称传递给回调,或者直接以闭包的形式写入您的函数。

回调参考

<?php  

function myPostCallback($http) {
  # code ...
}

$http = new Http;
$http->post('myPostCallback');

内联闭包

<?php  

$http = new Http;
$http->post(
  function ($http) {
    # code ...
  }
);

# also possible
$myGlobalVar = [1,2,3];
(new Http)
  ->get(
    function ($http) {
      # code ...
    }
  )
  ->post(
    function ($http, $myGlobalVar) {
      # code ...
    }
    , $myGlobalVar
  )
  ->exec();

要获取解析的请求体的值,请调用 Http\Request::get( string $key )

在编写回调时,您可以使用两种方法构建响应

  • Http\Response::set( string $key, mixed $value )

    参数
    • key: 要设置的值的名称
    • value: 要设置的值
  • Http\Response::set_array( array $array )

    参数
    • array: 要在响应中设置的值的关联数组

回调的最后一条语句将是调用 Http::send。这将发送响应后完全退出执行。

  • Http::send( [ int $statusCode = 200, string $contentType = "application/json", string $content = '' ] )

    参数
    • statusCode: 要返回的有效HTTP状态码
    • contentType: 要设置响应头的有效MIME类型
    • content: 如果您将Content-Type设置为非json,则可以使用此参数发送自定义数据。不会对此内容执行序列化。
    • 注意:任何未定义的路由都将返回状态码 405 和一个JSON格式的错误消息。
      {
        "error": "No route has been defined for this request method."
      }

如果您的错误处理中使用 try {} catch(Exception $e) {} 块,则可以在catch块中调用 Http::handleError( Exception $e ),它将自动以 500 状态码和包含错误的JSON有效负载进行回复。

一旦您定义了所有必要的HTTP方法回调,您只需通过调用来让您的 Http 实例运行适当的回调

<?php

$http->exec();

示例

<?php
# this example uses the Database and Html library as well
require_once 'path/to/vendor/autoload.php';

# alias our classes for cleanliness
use Database\MySQL;
use Http\Http;
use Html\Html;

# the callbacks for each http method
# get called with the instance of Http\Http
function get($http) {
  # instantiate our MySQL object with a connection config
  $db = new MySQL([
    'hostName'     => '1.1.1.1',
    'databaseName' => 'myDatabase',
    'dbUserName'   => 'admin',
    'dbPassword'   => 'adminPass',
  ]);

  # make a select query and pull $id from the request query string
  $db->query(
    "SELECT * FROM `sellers` WHERE `id` = {$http->request->query('id')}"
  )
  # this func gets called once for each row
  # 'use' pulls in $http from the closure's parent scope
  # sometimes we need to pass by reference like this: use( &$var )
  ->iterateResult(
    function ( $row ) use ( $http ){
      $http->response->set_array($row);
    }
  );
  # nesting operations to dumb the column names into response['sellers']
  $http->response->set( 'sellersColumns', $db->getColumns('sellers') );
  # test setting different types
  $http->response->set( 'test', [
    'one' => 1,
    'two' => 'two',
    'three' => true,
    'four' => [1,2,3],
  ]);
  # send what's in our response object
  $http->send();
}

# make our instance of Http\Http
$http = new Http;
# chain our calls together
$http
  ->get( 'get' )
  ->post(
    function ( $http ) {
      # code ...
    }
  )
  # there is a default exception handler,
  # but you can set a custom exception handler
  # like this:
  ->error(
    function ( Exception $e ) use ( $http ) {
      $http->send(500, 'text/html', new Html('code', $e));
    }
  )
  # execute the route
  ->exec();
<?php

require_once 'path/to/vendor/autoload.php';
use Http\Http;
use Http\Response;

Http::redirect('/index.html');
# this sends the location header & exits execution!
# the redirect location defaults to '/'

$current_http_status = Http::status();
# calling it without any arguments gets the current status code

$prev_http_status = Http::status(404);
# setting a new status will set the response code and return the old status

Http::status(Response::HTTP_NOT_FOUND);
# there are a number of constants available for valid status codes
# while it can be verbose, it can add readability to your code
# here is the fully namespaced list:
Http\Response::HTTP_CONTINUE = 100;
Http\Response::HTTP_SWITCHING_PROTOCOLS = 101;
Http\Response::HTTP_PROCESSING = 102;            // RFC2518
Http\Response::HTTP_OK = 200;
Http\Response::HTTP_CREATED = 201;
Http\Response::HTTP_ACCEPTED = 202;
Http\Response::HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
Http\Response::HTTP_NO_CONTENT = 204;
Http\Response::HTTP_RESET_CONTENT = 205;
Http\Response::HTTP_PARTIAL_CONTENT = 206;
Http\Response::HTTP_MULTI_STATUS = 207;          // RFC4918
Http\Response::HTTP_ALREADY_REPORTED = 208;      // RFC5842
Http\Response::HTTP_IM_USED = 226;               // RFC3229
Http\Response::HTTP_MULTIPLE_CHOICES = 300;
Http\Response::HTTP_MOVED_PERMANENTLY = 301;
Http\Response::HTTP_FOUND = 302;
Http\Response::HTTP_SEE_OTHER = 303;
Http\Response::HTTP_NOT_MODIFIED = 304;
Http\Response::HTTP_USE_PROXY = 305;
Http\Response::HTTP_RESERVED = 306;
Http\Response::HTTP_TEMPORARY_REDIRECT = 307;
Http\Response::HTTP_PERMANENTLY_REDIRECT = 308;  // RFC7238
Http\Response::HTTP_BAD_REQUEST = 400;
Http\Response::HTTP_UNAUTHORIZED = 401;
Http\Response::HTTP_PAYMENT_REQUIRED = 402;
Http\Response::HTTP_FORBIDDEN = 403;
Http\Response::HTTP_NOT_FOUND = 404;
Http\Response::HTTP_METHOD_NOT_ALLOWED = 405;
Http\Response::HTTP_NOT_ACCEPTABLE = 406;
Http\Response::HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
Http\Response::HTTP_REQUEST_TIMEOUT = 408;
Http\Response::HTTP_CONFLICT = 409;
Http\Response::HTTP_GONE = 410;
Http\Response::HTTP_LENGTH_REQUIRED = 411;
Http\Response::HTTP_PRECONDITION_FAILED = 412;
Http\Response::HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
Http\Response::HTTP_REQUEST_URI_TOO_LONG = 414;
Http\Response::HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
Http\Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
Http\Response::HTTP_EXPECTATION_FAILED = 417;
Http\Response::HTTP_I_AM_A_TEAPOT = 418;                                               // RFC2324
Http\Response::HTTP_MISDIRECTED_REQUEST = 421;                                         // RFC7540
Http\Response::HTTP_UNPROCESSABLE_ENTITY = 422;                                        // RFC4918
Http\Response::HTTP_LOCKED = 423;                                                      // RFC4918
Http\Response::HTTP_FAILED_DEPENDENCY = 424;                                           // RFC4918
Http\Response::HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425;   // RFC2817
Http\Response::HTTP_UPGRADE_REQUIRED = 426;                                            // RFC2817
Http\Response::HTTP_PRECONDITION_REQUIRED = 428;                                       // RFC6585
Http\Response::HTTP_TOO_MANY_REQUESTS = 429;                                           // RFC6585
Http\Response::HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;                             // RFC6585
Http\Response::HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
Http\Response::HTTP_INTERNAL_SERVER_ERROR = 500;
Http\Response::HTTP_NOT_IMPLEMENTED = 501;
Http\Response::HTTP_BAD_GATEWAY = 502;
Http\Response::HTTP_SERVICE_UNAVAILABLE = 503;
Http\Response::HTTP_GATEWAY_TIMEOUT = 504;
Http\Response::HTTP_VERSION_NOT_SUPPORTED = 505;
Http\Response::HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506;                        // RFC2295
Http\Response::HTTP_INSUFFICIENT_STORAGE = 507;                                        // RFC4918
Http\Response::HTTP_LOOP_DETECTED = 508;                                               // RFC5842
Http\Response::HTTP_NOT_EXTENDED = 510;                                                // RFC2774
Http\Response::HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;