json-api 系列的插件,简单好用,我喜欢.

调用都很简单:

http://xxx/api/get_info

怎么实现的?

插件初始化

核心是让 rewrite 生效.

// Add initialization and activation hooks
add_action('init', 'json_api_init');
register_activation_hook("$dir/json-api.php", 'json_api_activation');
register_deactivation_hook("$dir/json-api.php", 'json_api_deactivation');

function json_api_activation() {
  // Add the rewrite rule on activation
  global $wp_rewrite;
  add_filter('rewrite_rules_array', 'json_api_rewrites');
  $wp_rewrite->flush_rules();
}

function json_api_deactivation() {
  // Remove the rewrite rule on deactivation
  global $wp_rewrite;
  $wp_rewrite->flush_rules();
}

路由重写的方式:

add_filter('rewrite_rules_array', 'json_api_rewrites');

function json_api_rewrites($wp_rules) {
  $base = get_option('json_api_base', 'api');
  if (empty($base)) {
    return $wp_rules;
  }
  $json_api_rules = array(
    "$base\$" => 'index.php?json=info',
    "$base/(.+)\$" => 'index.php?json=$matches[1]'
  );
  return array_merge($json_api_rules, $wp_rules);
}

实际的实现,都是在原 wp 增加1个 json=的参数的基础上,比原生 rest 简单了很多倍.

JSON_API 对象

核心的 3 坨,也是 3 个对象:

$this->query = new JSON_API_Query();
$this->introspector = new JSON_API_Introspector();
$this->response = new JSON_API_Response();

query

即 class JSON_API_Query. query 是把 url 里参数转成了对象,方便使用. 核心动作是 get:

  function get($key)
  {
    if (is_array($key)) {
      $result = array();
      foreach ($key as $k) {
        $result[$k] = $this->get($k);
      }
      return $result;
    }
    $query_var = (isset($_REQUEST[$key])) ? $_REQUEST[$key] : null;
    $wp_query_var = $this->wp_query_var($key);
    if ($wp_query_var) {
      return $wp_query_var;
    } else if ($query_var) {
      return $this->strip_magic_quotes($query_var);
    } else if (isset($this->defaults[$key])) {
      return $this->defaults[$key];
    } else {
      return null;
    }
  }

各种找 key,核心是 wp_query_var,又是调了 wp 的get_query_var,也就是 wp 的查询入口,也可以说是查询类 restapi 的核心了,可查哪些东西? https://codex.wordpress.org/Class_Reference/WP_Query#Parameters

如何扩展

plugin 里处处都用的对象,自然是重点了.

json-api 把功能按 controller 分,可以很方便的扩展自己需要的 controller,比如核心的 core,posts,respond 等,后期扩展的 user 等,若自己要扩展也是按这个套路.

具体怎么做的?非常简单粗暴…

controllers 文件夹下,如果定义了class JSON_API_XXX_Controller的类,那么就识别为1个 controller.


  function check_directory_for_controllers($dir, &$controllers) {
    $dh = opendir($dir);
    while ($file = readdir($dh)) {
      if (preg_match('/(.+)\.php$/i', $file, $matches)) {
        $src = file_get_contents("$dir/$file");
        if (preg_match("/class\s+JSON_API_{$matches[1]}_Controller/i", $src)) {
          $controllers[] = $matches[1];
        }
      }
    }
  }

具体在哪里执行

template_redirect注册了个钩子:

add_action('template_redirect', array(&$this, 'template_redirect'));

function template_redirect() {
    // Check to see if there's an appropriate API controller + method
    $controller = strtolower($this->query->get_controller());
    $available_controllers = $this->get_controllers();
    $enabled_controllers = explode(',', get_option('json_api_controllers', 'core'));
    $active_controllers = array_intersect($available_controllers, $enabled_controllers);

    if ($controller) {

      if (empty($this->query->dev)) {
        error_reporting(0);
      }

      if (!in_array($controller, $active_controllers)) {
        $this->error("Unknown controller '$controller'.");
      }

      $controller_path = $this->controller_path($controller);
      if (file_exists($controller_path)) {
        require_once $controller_path;
      }
      $controller_class = $this->controller_class($controller);

      if (!class_exists($controller_class)) {
        $this->error("Unknown controller '$controller_class'.");
      }

      $this->controller = new $controller_class();
      $method = $this->query->get_method($controller);

      if ($method) {

        $this->response->setup();

        // Run action hooks for method
        //如果没有hook,就是创建钩子.
        do_action("json_api", $controller, $method);
        do_action("json_api-{$controller}-$method");

        // Error out if nothing is found
        if ($method == '404') {
          $this->error('Not found');
        }

        // Run the method
        $result = $this->controller->$method();
        // Handle the result
        $this->response->respond($result);
        // Done!
        exit;
      }
    }
}