我认为最好的选择是端点。您可以将所有数据作为一个简单的字符串来获取,因此您可以决定如何解析它,并且不必担心与其他重写规则的冲突。
关于端点,我学到了一件事:让主要工作尽可能抽象,以数据不可知的方式修复WordPress API中的故障。
我将把逻辑分为三个部分:acontroller 选择模型和视图model 处理端点和一个或多个views 返回一些有用的数据或错误消息。
控制器让我们从控制器开始。它做的不多,所以我在这里使用一个非常简单的函数:
add_action( \'plugins_loaded\', \'t5_cra_init\' );
function t5_cra_init()
{
require dirname( __FILE__ ) . \'/class.T5_CRA_Model.php\';
$options = array (
\'callback\' => array ( \'T5_CRA_View_Demo\', \'__construct\' ),
\'name\' => \'api\',
\'position\' => EP_ROOT
);
new T5_CRA_Model( $options );
}
基本上,它加载模型
T5_CRA_Model
交一些参数…还有所有的工作。控制器对模型或视图的内部逻辑一无所知。它只是把两者粘在一起。这是唯一不能重复使用的部分;这就是为什么我把它和其他部分分开。
现在我们至少需要两门课:model 注册API和view 创建输出。
该类车型将:
注册端点捕获在没有任何附加参数的情况下调用端点的情况,填充由于第三方代码中的某些错误而丢失的重写规则,修复WordPress与静态首页和端点的故障EP_ROOT
将URI解析为一个数组(这也可以分开)使用这些值调用回调处理程序,我希望代码能说明问题。:)模型对数据的内部结构或表示形式一无所知。因此,您可以使用它注册数百个API,而无需更改一行。
<?php # -*- coding: utf-8 -*-
/**
* Register new REST API as endpoint.
*
* @author toscho http://toscho.de
*
*/
class T5_CRA_Model
{
protected $options;
/**
* Read options and register endpoint actions and filters.
*
* @wp-hook plugins_loaded
* @param array $options
*/
public function __construct( Array $options )
{
$default_options = array (
\'callback\' => array ( \'T5_CRA_View_Demo\', \'__construct\' ),
\'name\' => \'api\',
\'position\' => EP_ROOT
);
$this->options = wp_parse_args( $options, $default_options );
add_action( \'init\', array ( $this, \'register_api\' ), 1000 );
// endpoints work on the front end only
if ( is_admin() )
return;
add_filter( \'request\', array ( $this, \'set_query_var\' ) );
// Hook in late to allow other plugins to operate earlier.
add_action( \'template_redirect\', array ( $this, \'render\' ), 100 );
}
/**
* Add endpoint and deal with other code flushing our rules away.
*
* @wp-hook init
* @return void
*/
public function register_api()
{
add_rewrite_endpoint(
$this->options[\'name\'],
$this->options[\'position\']
);
$this->fix_failed_registration(
$this->options[\'name\'],
$this->options[\'position\']
);
}
/**
* Fix rules flushed by other peoples code.
*
* @wp-hook init
* @param string $name
* @param int $position
*/
protected function fix_failed_registration( $name, $position )
{
global $wp_rewrite;
if ( empty ( $wp_rewrite->endpoints ) )
return flush_rewrite_rules( FALSE );
foreach ( $wp_rewrite->endpoints as $endpoint )
if ( $endpoint[0] === $position && $endpoint[1] === $name )
return;
flush_rewrite_rules( FALSE );
}
/**
* Set the endpoint variable to TRUE.
*
* If the endpoint was called without further parameters it does not
* evaluate to TRUE otherwise.
*
* @wp-hook request
* @param array $vars
* @return array
*/
public function set_query_var( Array $vars )
{
if ( ! empty ( $vars[ $this->options[\'name\'] ] ) )
return $vars;
// When a static page was set as front page, the WordPress endpoint API
// does some strange things. Let\'s fix that.
if ( isset ( $vars[ $this->options[\'name\'] ] )
or ( isset ( $vars[\'pagename\'] ) and $this->options[\'name\'] === $vars[\'pagename\'] )
or ( isset ( $vars[\'page\'] ) and $this->options[\'name\'] === $vars[\'name\'] )
)
{
// In some cases WP misinterprets the request as a page request and
// returns a 404.
$vars[\'page\'] = $vars[\'pagename\'] = $vars[\'name\'] = FALSE;
$vars[ $this->options[\'name\'] ] = TRUE;
}
return $vars;
}
/**
* Prepare API requests and hand them over to the callback.
*
* @wp-hook template_redirect
* @return void
*/
public function render()
{
$api = get_query_var( $this->options[\'name\'] );
$api = trim( $api, \'/\' );
if ( \'\' === $api )
return;
$parts = explode( \'/\', $api );
$type = array_shift( $parts );
$values = $this->get_api_values( join( \'/\', $parts ) );
$callback = $this->options[\'callback\'];
if ( is_string( $callback ) )
{
call_user_func( $callback, $type, $values );
}
elseif ( is_array( $callback ) )
{
if ( \'__construct\' === $callback[1] )
new $callback[0]( $type, $values );
elseif ( is_callable( $callback ) )
call_user_func( $callback, $type, $values );
}
else
{
trigger_error(
\'Cannot call your callback: \' . var_export( $callback, TRUE ),
E_USER_ERROR
);
}
// Important. WordPress will render the main page if we leave this out.
exit;
}
/**
* Parse request URI into associative array.
*
* @wp-hook template_redirect
* @param string $request
* @return array
*/
protected function get_api_values( $request )
{
$keys = $values = array();
$count = 0;
$request = trim( $request, \'/\' );
$tok = strtok( $request, \'/\' );
while ( $tok !== FALSE )
{
0 === $count++ % 2 ? $keys[] = $tok : $values[] = $tok;
$tok = strtok( \'/\' );
}
// fix odd requests
if ( count( $keys ) !== count( $values ) )
$values[] = \'\';
return array_combine( $keys, $values );
}
}
视图现在我们必须对数据进行处理。我们还可以捕获不完整请求的缺失数据,或者将处理委托给其他视图或子控制器。下面是一个非常简单的示例:
class T5_CRA_View_Demo
{
protected $allowed_types = array (
\'plain\',
\'html\',
\'xml\'
);
protected $default_values = array (
\'country\' => \'Norway\',
\'date\' => 1700,
\'max\' => 200
);
public function __construct( $type, $data )
{
if ( ! in_array( $type, $this->allowed_types ) )
die( \'Your request is invalid. Please read our fantastic manual.\' );
$data = wp_parse_args( $data, $this->default_values );
header( "Content-Type: text/$type;charset=utf-8" );
$method = "render_$type";
$this->$method( $data );
}
protected function render_plain( $data )
{
foreach ( $data as $key => $value )
print "$key: $value\\n";
}
protected function render_html( $data ) {}
protected function render_xml( $data ) {}
}
重要的部分是:视图对端点一无所知。您可以使用它来处理完全不同的请求,例如wp-admin
. 您可以将视图拆分为自己的MVC模式,或者只使用一个简单的函数。