wordpress-advanced-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdvanced WordPress Architecture
高级WordPress架构
Master advanced WordPress development patterns including REST API endpoints, WP-CLI commands, performance optimization, and caching strategies for scalable WordPress applications.
掌握高级WordPress开发模式,包括REST API端点、WP-CLI命令、性能优化以及面向可扩展WordPress应用的缓存策略。
1. REST API Development
1. REST API开发
The WordPress REST API provides a powerful interface for creating custom endpoints with proper authentication, validation, and response formatting.
WordPress REST API提供了一个强大的接口,可用于创建具备适当身份验证、验证和响应格式的自定义端点。
Endpoint Registration with Namespacing
带命名空间的端点注册
php
add_action( 'rest_api_init', 'register_custom_rest_routes' );
function register_custom_rest_routes() {
// Namespace: myplugin/v1 (enables versioning)
$namespace = 'myplugin/v1';
// GET /wp-json/myplugin/v1/books
register_rest_route( $namespace, '/books', [
'methods' => 'GET',
'callback' => 'get_books_callback',
'permission_callback' => '__return_true', // Public endpoint
'args' => [
'per_page' => [
'default' => 10,
'validate_callback' => function( $param ) {
return is_numeric( $param ) && $param > 0 && $param <= 100;
},
'sanitize_callback' => 'absint',
],
'page' => [
'default' => 1,
'validate_callback' => function( $param ) {
return is_numeric( $param ) && $param > 0;
},
'sanitize_callback' => 'absint',
],
],
]);
// GET /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => 'get_book_callback',
'permission_callback' => '__return_true',
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
],
]);
// POST /wp-json/myplugin/v1/books (authenticated)
register_rest_route( $namespace, '/books', [
'methods' => 'POST',
'callback' => 'create_book_callback',
'permission_callback' => function() {
return current_user_can( 'edit_posts' );
},
'args' => [
'title' => [
'required' => true,
'type' => 'string',
'validate_callback' => function( $param ) {
return is_string( $param ) && strlen( $param ) > 0;
},
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'required' => false,
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
],
'status' => [
'default' => 'draft',
'enum' => [ 'draft', 'publish', 'private' ],
],
],
]);
// PUT /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'PUT',
'callback' => 'update_book_callback',
'permission_callback' => function( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'edit_post', $book_id );
},
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
'title' => [
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
],
],
]);
// DELETE /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'DELETE',
'callback' => 'delete_book_callback',
'permission_callback' => function( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'delete_post', $book_id );
},
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
],
]);
}php
add_action( 'rest_api_init', 'register_custom_rest_routes' );
function register_custom_rest_routes() {
// Namespace: myplugin/v1 (enables versioning)
$namespace = 'myplugin/v1';
// GET /wp-json/myplugin/v1/books
register_rest_route( $namespace, '/books', [
'methods' => 'GET',
'callback' => 'get_books_callback',
'permission_callback' => '__return_true', // Public endpoint
'args' => [
'per_page' => [
'default' => 10,
'validate_callback' => function( $param ) {
return is_numeric( $param ) && $param > 0 && $param <= 100;
},
'sanitize_callback' => 'absint',
],
'page' => [
'default' => 1,
'validate_callback' => function( $param ) {
return is_numeric( $param ) && $param > 0;
},
'sanitize_callback' => 'absint',
],
],
]);
// GET /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => 'get_book_callback',
'permission_callback' => '__return_true',
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
],
]);
// POST /wp-json/myplugin/v1/books (authenticated)
register_rest_route( $namespace, '/books', [
'methods' => 'POST',
'callback' => 'create_book_callback',
'permission_callback' => function() {
return current_user_can( 'edit_posts' );
},
'args' => [
'title' => [
'required' => true,
'type' => 'string',
'validate_callback' => function( $param ) {
return is_string( $param ) && strlen( $param ) > 0;
},
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'required' => false,
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
],
'status' => [
'default' => 'draft',
'enum' => [ 'draft', 'publish', 'private' ],
],
],
]);
// PUT /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'PUT',
'callback' => 'update_book_callback',
'permission_callback' => function( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'edit_post', $book_id );
},
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
'title' => [
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
],
],
]);
// DELETE /wp-json/myplugin/v1/books/(?P<id>\d+)
register_rest_route( $namespace, '/books/(?P<id>\d+)', [
'methods' => 'DELETE',
'callback' => 'delete_book_callback',
'permission_callback' => function( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'delete_post', $book_id );
},
'args' => [
'id' => [
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
'sanitize_callback' => 'absint',
],
],
]);
}Complete CRUD Implementation
完整CRUD实现
php
// GET /wp-json/myplugin/v1/books
function get_books_callback( $request ) {
$per_page = $request->get_param( 'per_page' );
$page = $request->get_param( 'page' );
$offset = ( $page - 1 ) * $per_page;
$args = [
'post_type' => 'book',
'posts_per_page' => $per_page,
'offset' => $offset,
'post_status' => 'publish',
];
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) {
return rest_ensure_response([
'books' => [],
'total' => 0,
'page' => $page,
'per_page' => $per_page,
]);
}
$books = [];
while ( $query->have_posts() ) {
$query->the_post();
$books[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'content' => get_the_content(),
'author' => get_the_author(),
'date' => get_the_date( 'c' ), // ISO 8601 format
'link' => get_permalink(),
];
}
wp_reset_postdata();
$response = rest_ensure_response([
'books' => $books,
'total' => $query->found_posts,
'page' => $page,
'per_page' => $per_page,
'total_pages' => ceil( $query->found_posts / $per_page ),
]);
// Add HATEOAS links
$response->add_link( 'self', rest_url( "myplugin/v1/books?page={$page}&per_page={$per_page}" ) );
if ( $page > 1 ) {
$prev_page = $page - 1;
$response->add_link( 'prev', rest_url( "myplugin/v1/books?page={$prev_page}&per_page={$per_page}" ) );
}
if ( $page < ceil( $query->found_posts / $per_page ) ) {
$next_page = $page + 1;
$response->add_link( 'next', rest_url( "myplugin/v1/books?page={$next_page}&per_page={$per_page}" ) );
}
return $response;
}
// GET /wp-json/myplugin/v1/books/123
function get_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
$data = [
'id' => $book->ID,
'title' => $book->post_title,
'content' => apply_filters( 'the_content', $book->post_content ),
'excerpt' => $book->post_excerpt,
'author' => get_the_author_meta( 'display_name', $book->post_author ),
'date' => get_the_date( 'c', $book ),
'modified' => get_the_modified_date( 'c', $book ),
'status' => $book->post_status,
'link' => get_permalink( $book ),
'featured_image' => get_the_post_thumbnail_url( $book, 'large' ),
'meta' => [
'isbn' => get_post_meta( $book->ID, '_isbn', true ),
'pages' => (int) get_post_meta( $book->ID, '_pages', true ),
],
];
return rest_ensure_response( $data );
}
// POST /wp-json/myplugin/v1/books
function create_book_callback( $request ) {
$title = $request->get_param( 'title' );
$content = $request->get_param( 'content' );
$status = $request->get_param( 'status' );
$post_data = [
'post_type' => 'book',
'post_title' => $title,
'post_content' => $content,
'post_status' => $status,
'post_author' => get_current_user_id(),
];
$book_id = wp_insert_post( $post_data, true );
if ( is_wp_error( $book_id ) ) {
return new WP_Error(
'book_creation_failed',
$book_id->get_error_message(),
[ 'status' => 500 ]
);
}
// Add custom metadata if provided
if ( $request->has_param( 'isbn' ) ) {
update_post_meta( $book_id, '_isbn', sanitize_text_field( $request->get_param( 'isbn' ) ) );
}
if ( $request->has_param( 'pages' ) ) {
update_post_meta( $book_id, '_pages', absint( $request->get_param( 'pages' ) ) );
}
$response = rest_ensure_response([
'id' => $book_id,
'title' => $title,
'message' => 'Book created successfully',
'link' => get_permalink( $book_id ),
]);
$response->set_status( 201 ); // Created
$response->header( 'Location', rest_url( "myplugin/v1/books/{$book_id}" ) );
return $response;
}
// PUT /wp-json/myplugin/v1/books/123
function update_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
$post_data = [ 'ID' => $book_id ];
if ( $request->has_param( 'title' ) ) {
$post_data['post_title'] = $request->get_param( 'title' );
}
if ( $request->has_param( 'content' ) ) {
$post_data['post_content'] = $request->get_param( 'content' );
}
if ( $request->has_param( 'status' ) ) {
$post_data['post_status'] = $request->get_param( 'status' );
}
$result = wp_update_post( $post_data, true );
if ( is_wp_error( $result ) ) {
return new WP_Error(
'book_update_failed',
$result->get_error_message(),
[ 'status' => 500 ]
);
}
return rest_ensure_response([
'id' => $book_id,
'message' => 'Book updated successfully',
'link' => get_permalink( $book_id ),
]);
}
// DELETE /wp-json/myplugin/v1/books/123
function delete_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
// Soft delete (trash) or hard delete
$force = $request->get_param( 'force' );
$result = wp_delete_post( $book_id, $force );
if ( ! $result ) {
return new WP_Error(
'book_deletion_failed',
'Failed to delete book',
[ 'status' => 500 ]
);
}
return rest_ensure_response([
'deleted' => true,
'id' => $book_id,
'message' => $force ? 'Book permanently deleted' : 'Book moved to trash',
]);
}php
// GET /wp-json/myplugin/v1/books
function get_books_callback( $request ) {
$per_page = $request->get_param( 'per_page' );
$page = $request->get_param( 'page' );
$offset = ( $page - 1 ) * $per_page;
$args = [
'post_type' => 'book',
'posts_per_page' => $per_page,
'offset' => $offset,
'post_status' => 'publish',
];
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) {
return rest_ensure_response([
'books' => [],
'total' => 0,
'page' => $page,
'per_page' => $per_page,
]);
}
$books = [];
while ( $query->have_posts() ) {
$query->the_post();
$books[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'content' => get_the_content(),
'author' => get_the_author(),
'date' => get_the_date( 'c' ), // ISO 8601 format
'link' => get_permalink(),
];
}
wp_reset_postdata();
$response = rest_ensure_response([
'books' => $books,
'total' => $query->found_posts,
'page' => $page,
'per_page' => $per_page,
'total_pages' => ceil( $query->found_posts / $per_page ),
]);
// Add HATEOAS links
$response->add_link( 'self', rest_url( "myplugin/v1/books?page={$page}&per_page={$per_page}" ) );
if ( $page > 1 ) {
$prev_page = $page - 1;
$response->add_link( 'prev', rest_url( "myplugin/v1/books?page={$prev_page}&per_page={$per_page}" ) );
}
if ( $page < ceil( $query->found_posts / $per_page ) ) {
$next_page = $page + 1;
$response->add_link( 'next', rest_url( "myplugin/v1/books?page={$next_page}&per_page={$per_page}" ) );
}
return $response;
}
// GET /wp-json/myplugin/v1/books/123
function get_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
$data = [
'id' => $book->ID,
'title' => $book->post_title,
'content' => apply_filters( 'the_content', $book->post_content ),
'excerpt' => $book->post_excerpt,
'author' => get_the_author_meta( 'display_name', $book->post_author ),
'date' => get_the_date( 'c', $book ),
'modified' => get_the_modified_date( 'c', $book ),
'status' => $book->post_status,
'link' => get_permalink( $book ),
'featured_image' => get_the_post_thumbnail_url( $book, 'large' ),
'meta' => [
'isbn' => get_post_meta( $book->ID, '_isbn', true ),
'pages' => (int) get_post_meta( $book->ID, '_pages', true ),
],
];
return rest_ensure_response( $data );
}
// POST /wp-json/myplugin/v1/books
function create_book_callback( $request ) {
$title = $request->get_param( 'title' );
$content = $request->get_param( 'content' );
$status = $request->get_param( 'status' );
$post_data = [
'post_type' => 'book',
'post_title' => $title,
'post_content' => $content,
'post_status' => $status,
'post_author' => get_current_user_id(),
];
$book_id = wp_insert_post( $post_data, true );
if ( is_wp_error( $book_id ) ) {
return new WP_Error(
'book_creation_failed',
$book_id->get_error_message(),
[ 'status' => 500 ]
);
}
// Add custom metadata if provided
if ( $request->has_param( 'isbn' ) ) {
update_post_meta( $book_id, '_isbn', sanitize_text_field( $request->get_param( 'isbn' ) ) );
}
if ( $request->has_param( 'pages' ) ) {
update_post_meta( $book_id, '_pages', absint( $request->get_param( 'pages' ) ) );
}
$response = rest_ensure_response([
'id' => $book_id,
'title' => $title,
'message' => 'Book created successfully',
'link' => get_permalink( $book_id ),
]);
$response->set_status( 201 ); // Created
$response->header( 'Location', rest_url( "myplugin/v1/books/{$book_id}" ) );
return $response;
}
// PUT /wp-json/myplugin/v1/books/123
function update_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
$post_data = [ 'ID' => $book_id ];
if ( $request->has_param( 'title' ) ) {
$post_data['post_title'] = $request->get_param( 'title' );
}
if ( $request->has_param( 'content' ) ) {
$post_data['post_content'] = $request->get_param( 'content' );
}
if ( $request->has_param( 'status' ) ) {
$post_data['post_status'] = $request->get_param( 'status' );
}
$result = wp_update_post( $post_data, true );
if ( is_wp_error( $result ) ) {
return new WP_Error(
'book_update_failed',
$result->get_error_message(),
[ 'status' => 500 ]
);
}
return rest_ensure_response([
'id' => $book_id,
'message' => 'Book updated successfully',
'link' => get_permalink( $book_id ),
]);
}
// DELETE /wp-json/myplugin/v1/books/123
function delete_book_callback( $request ) {
$book_id = $request->get_param( 'id' );
$book = get_post( $book_id );
if ( ! $book || 'book' !== $book->post_type ) {
return new WP_Error(
'book_not_found',
'Book not found',
[ 'status' => 404 ]
);
}
// Soft delete (trash) or hard delete
$force = $request->get_param( 'force' );
$result = wp_delete_post( $book_id, $force );
if ( ! $result ) {
return new WP_Error(
'book_deletion_failed',
'Failed to delete book',
[ 'status' => 500 ]
);
}
return rest_ensure_response([
'deleted' => true,
'id' => $book_id,
'message' => $force ? 'Book permanently deleted' : 'Book moved to trash',
]);
}Controller Pattern for Complex Endpoints
复杂端点的控制器模式
For complex REST endpoints, use a controller class to organize logic:
php
<?php
namespace MyPlugin\API;
class Books_Controller extends \WP_REST_Controller {
protected $namespace = 'myplugin/v1';
protected $rest_base = 'books';
public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_items' ],
'permission_callback' => [ $this, 'get_items_permissions_check' ],
'args' => $this->get_collection_params(),
],
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [ $this, 'create_item' ],
'permission_callback' => [ $this, 'create_item_permissions_check' ],
'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
],
'schema' => [ $this, 'get_public_item_schema' ],
]);
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
'args' => [
'id' => [
'description' => 'Unique identifier for the book',
'type' => 'integer',
],
],
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_item' ],
'permission_callback' => [ $this, 'get_item_permissions_check' ],
'args' => [
'context' => $this->get_context_param( [ 'default' => 'view' ] ),
],
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ $this, 'update_item' ],
'permission_callback' => [ $this, 'update_item_permissions_check' ],
'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
],
[
'methods' => \WP_REST_Server::DELETABLE,
'callback' => [ $this, 'delete_item' ],
'permission_callback' => [ $this, 'delete_item_permissions_check' ],
'args' => [
'force' => [
'type' => 'boolean',
'default' => false,
'description' => 'Whether to bypass trash and force deletion',
],
],
],
'schema' => [ $this, 'get_public_item_schema' ],
]);
}
public function get_items( $request ) {
// Implementation similar to get_books_callback above
}
public function get_item( $request ) {
// Implementation similar to get_book_callback above
}
public function create_item( $request ) {
// Implementation similar to create_book_callback above
}
public function update_item( $request ) {
// Implementation similar to update_book_callback above
}
public function delete_item( $request ) {
// Implementation similar to delete_book_callback above
}
public function get_items_permissions_check( $request ) {
return true; // Public endpoint
}
public function get_item_permissions_check( $request ) {
return true; // Public endpoint
}
public function create_item_permissions_check( $request ) {
return current_user_can( 'edit_posts' );
}
public function update_item_permissions_check( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'edit_post', $book_id );
}
public function delete_item_permissions_check( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'delete_post', $book_id );
}
public function get_public_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$schema = [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'book',
'type' => 'object',
'properties' => [
'id' => [
'description' => 'Unique identifier for the book',
'type' => 'integer',
'context' => [ 'view', 'edit', 'embed' ],
'readonly' => true,
],
'title' => [
'description' => 'The book title',
'type' => 'string',
'context' => [ 'view', 'edit', 'embed' ],
'required' => true,
],
'content' => [
'description' => 'The book content',
'type' => 'string',
'context' => [ 'view', 'edit' ],
],
'status' => [
'description' => 'The book status',
'type' => 'string',
'enum' => [ 'draft', 'publish', 'private' ],
'context' => [ 'view', 'edit' ],
],
],
];
$this->schema = $schema;
return $this->add_additional_fields_schema( $this->schema );
}
}
// Register controller
add_action( 'rest_api_init', function() {
$controller = new \MyPlugin\API\Books_Controller();
$controller->register_routes();
});对于复杂的REST端点,使用控制器类来组织逻辑:
php
<?php
namespace MyPlugin\API;
class Books_Controller extends \WP_REST_Controller {
protected $namespace = 'myplugin/v1';
protected $rest_base = 'books';
public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_items' ],
'permission_callback' => [ $this, 'get_items_permissions_check' ],
'args' => $this->get_collection_params(),
],
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [ $this, 'create_item' ],
'permission_callback' => [ $this, 'create_item_permissions_check' ],
'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
],
'schema' => [ $this, 'get_public_item_schema' ],
]);
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
'args' => [
'id' => [
'description' => 'Unique identifier for the book',
'type' => 'integer',
],
],
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_item' ],
'permission_callback' => [ $this, 'get_item_permissions_check' ],
'args' => [
'context' => $this->get_context_param( [ 'default' => 'view' ] ),
],
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ $this, 'update_item' ],
'permission_callback' => [ $this, 'update_item_permissions_check' ],
'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
],
[
'methods' => \WP_REST_Server::DELETABLE,
'callback' => [ $this, 'delete_item' ],
'permission_callback' => [ $this, 'delete_item_permissions_check' ],
'args' => [
'force' => [
'type' => 'boolean',
'default' => false,
'description' => 'Whether to bypass trash and force deletion',
],
],
],
'schema' => [ $this, 'get_public_item_schema' ],
]);
}
public function get_items( $request ) {
// Implementation similar to get_books_callback above
}
public function get_item( $request ) {
// Implementation similar to get_book_callback above
}
public function create_item( $request ) {
// Implementation similar to create_book_callback above
}
public function update_item( $request ) {
// Implementation similar to update_book_callback above
}
public function delete_item( $request ) {
// Implementation similar to delete_book_callback above
}
public function get_items_permissions_check( $request ) {
return true; // Public endpoint
}
public function get_item_permissions_check( $request ) {
return true; // Public endpoint
}
public function create_item_permissions_check( $request ) {
return current_user_can( 'edit_posts' );
}
public function update_item_permissions_check( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'edit_post', $book_id );
}
public function delete_item_permissions_check( $request ) {
$book_id = $request->get_param( 'id' );
return current_user_can( 'delete_post', $book_id );
}
public function get_public_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$schema = [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'book',
'type' => 'object',
'properties' => [
'id' => [
'description' => 'Unique identifier for the book',
'type' => 'integer',
'context' => [ 'view', 'edit', 'embed' ],
'readonly' => true,
],
'title' => [
'description' => 'The book title',
'type' => 'string',
'context' => [ 'view', 'edit', 'embed' ],
'required' => true,
],
'content' => [
'description' => 'The book content',
'type' => 'string',
'context' => [ 'view', 'edit' ],
],
'status' => [
'description' => 'The book status',
'type' => 'string',
'enum' => [ 'draft', 'publish', 'private' ],
'context' => [ 'view', 'edit' ],
],
],
];
$this->schema = $schema;
return $this->add_additional_fields_schema( $this->schema );
}
}
// Register controller
add_action( 'rest_api_init', function() {
$controller = new \MyPlugin\API\Books_Controller();
$controller->register_routes();
});REST API Authentication
REST API身份验证
php
// Application Passwords (WordPress 5.6+)
// Users generate passwords at /wp-admin/profile.php
// Example cURL request with authentication:
// curl -X POST https://example.com/wp-json/myplugin/v1/books \
// -u username:application_password \
// -H "Content-Type: application/json" \
// -d '{"title":"New Book","content":"Book content"}'
// Cookie authentication for logged-in users (requires nonce)
add_action( 'rest_api_init', function() {
// Add nonce to wp_localize_script
wp_localize_script( 'my-ajax-script', 'wpApiSettings', [
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' ),
]);
});
// JavaScript REST API call with nonce:
/*
fetch( wpApiSettings.root + 'myplugin/v1/books', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce
},
body: JSON.stringify({
title: 'New Book',
content: 'Book content'
})
}).then( response => response.json() )
.then( data => console.log( data ) );
*/php
// Application Passwords (WordPress 5.6+)
// Users generate passwords at /wp-admin/profile.php
// Example cURL request with authentication:
// curl -X POST https://example.com/wp-json/myplugin/v1/books \
// -u username:application_password \
// -H "Content-Type: application/json" \
// -d '{"title":"New Book","content":"Book content"}'
// Cookie authentication for logged-in users (requires nonce)
add_action( 'rest_api_init', function() {
// Add nonce to wp_localize_script
wp_localize_script( 'my-ajax-script', 'wpApiSettings', [
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' ),
]);
});
// JavaScript REST API call with nonce:
/*
fetch( wpApiSettings.root + 'myplugin/v1/books', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce
},
body: JSON.stringify({
title: 'New Book',
content: 'Book content'
})
}).then( response => response.json() )
.then( data => console.log( data ) );
*/2. WP-CLI Commands
2. WP-CLI命令
WP-CLI enables automation of WordPress tasks through custom commands.
WP-CLI支持通过自定义命令自动化WordPress任务。
Custom Command Registration
自定义命令注册
php
<?php
namespace MyPlugin\CLI;
class Books_Command {
/**
* List all books.
*
* ## OPTIONS
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - yaml
* ---
*
* [--status=<status>]
* : Filter by post status.
*
* ## EXAMPLES
*
* wp books list
* wp books list --format=json
* wp books list --status=publish
*
* @when after_wp_load
*/
public function list( $args, $assoc_args ) {
$defaults = [
'format' => 'table',
'status' => 'any',
];
$assoc_args = wp_parse_args( $assoc_args, $defaults );
$query_args = [
'post_type' => 'book',
'posts_per_page' => -1,
'post_status' => $assoc_args['status'],
];
$books = get_posts( $query_args );
if ( empty( $books ) ) {
\WP_CLI::warning( 'No books found.' );
return;
}
$items = [];
foreach ( $books as $book ) {
$items[] = [
'ID' => $book->ID,
'Title' => $book->post_title,
'Status' => $book->post_status,
'Author' => get_the_author_meta( 'display_name', $book->post_author ),
'Date' => get_the_date( 'Y-m-d H:i:s', $book ),
];
}
\WP_CLI\Utils\format_items( $assoc_args['format'], $items, [ 'ID', 'Title', 'Status', 'Author', 'Date' ] );
\WP_CLI::success( sprintf( 'Found %d books.', count( $items ) ) );
}
/**
* Create a new book.
*
* ## OPTIONS
*
* <title>
* : The book title.
*
* [--content=<content>]
* : The book content.
*
* [--status=<status>]
* : The book status.
* ---
* default: draft
* options:
* - draft
* - publish
* - private
* ---
*
* [--isbn=<isbn>]
* : The book ISBN.
*
* [--pages=<pages>]
* : Number of pages.
*
* ## EXAMPLES
*
* wp books create "My Book Title"
* wp books create "My Book" --status=publish --isbn=978-3-16-148410-0 --pages=350
*
* @when after_wp_load
*/
public function create( $args, $assoc_args ) {
$title = $args[0];
$defaults = [
'content' => '',
'status' => 'draft',
];
$assoc_args = wp_parse_args( $assoc_args, $defaults );
$post_data = [
'post_type' => 'book',
'post_title' => $title,
'post_content' => $assoc_args['content'],
'post_status' => $assoc_args['status'],
'post_author' => get_current_user_id(),
];
$book_id = wp_insert_post( $post_data, true );
if ( is_wp_error( $book_id ) ) {
\WP_CLI::error( 'Failed to create book: ' . $book_id->get_error_message() );
}
// Add custom metadata
if ( isset( $assoc_args['isbn'] ) ) {
update_post_meta( $book_id, '_isbn', sanitize_text_field( $assoc_args['isbn'] ) );
}
if ( isset( $assoc_args['pages'] ) ) {
update_post_meta( $book_id, '_pages', absint( $assoc_args['pages'] ) );
}
\WP_CLI::success( sprintf( 'Created book #%d: %s', $book_id, $title ) );
}
/**
* Import books from CSV file.
*
* ## OPTIONS
*
* <file>
* : Path to CSV file.
*
* [--dry-run]
* : Preview import without creating books.
*
* ## EXAMPLES
*
* wp books import books.csv
* wp books import books.csv --dry-run
*
* @when after_wp_load
*/
public function import( $args, $assoc_args ) {
$file = $args[0];
$dry_run = isset( $assoc_args['dry-run'] );
if ( ! file_exists( $file ) ) {
\WP_CLI::error( 'File not found: ' . $file );
}
$csv = array_map( 'str_getcsv', file( $file ) );
$header = array_shift( $csv );
$total = count( $csv );
$created = 0;
\WP_CLI::log( sprintf( 'Processing %d books...', $total ) );
$progress = \WP_CLI\Utils\make_progress_bar( 'Importing books', $total );
foreach ( $csv as $row ) {
$book = array_combine( $header, $row );
if ( $dry_run ) {
\WP_CLI::log( sprintf( 'Would create: %s', $book['title'] ) );
} else {
$post_data = [
'post_type' => 'book',
'post_title' => $book['title'],
'post_content' => $book['content'] ?? '',
'post_status' => $book['status'] ?? 'draft',
];
$book_id = wp_insert_post( $post_data, true );
if ( ! is_wp_error( $book_id ) ) {
if ( isset( $book['isbn'] ) ) {
update_post_meta( $book_id, '_isbn', $book['isbn'] );
}
$created++;
}
}
$progress->tick();
}
$progress->finish();
if ( $dry_run ) {
\WP_CLI::success( sprintf( 'Dry run complete. Would create %d books.', $total ) );
} else {
\WP_CLI::success( sprintf( 'Imported %d of %d books.', $created, $total ) );
}
}
/**
* Generate sample books.
*
* ## OPTIONS
*
* [--count=<count>]
* : Number of books to generate.
* ---
* default: 10
* ---
*
* [--status=<status>]
* : Book status.
* ---
* default: publish
* ---
*
* ## EXAMPLES
*
* wp books generate --count=50
* wp books generate --count=100 --status=draft
*
* @when after_wp_load
*/
public function generate( $args, $assoc_args ) {
$count = isset( $assoc_args['count'] ) ? absint( $assoc_args['count'] ) : 10;
$status = isset( $assoc_args['status'] ) ? $assoc_args['status'] : 'publish';
$progress = \WP_CLI\Utils\make_progress_bar( 'Generating books', $count );
for ( $i = 1; $i <= $count; $i++ ) {
$post_data = [
'post_type' => 'book',
'post_title' => sprintf( 'Sample Book %d', $i ),
'post_content' => sprintf( 'This is sample book number %d.', $i ),
'post_status' => $status,
];
$book_id = wp_insert_post( $post_data );
// Add random metadata
update_post_meta( $book_id, '_isbn', sprintf( '978-3-16-%06d-0', rand( 100000, 999999 ) ) );
update_post_meta( $book_id, '_pages', rand( 100, 500 ) );
$progress->tick();
}
$progress->finish();
\WP_CLI::success( sprintf( 'Generated %d books.', $count ) );
}
}
// Register WP-CLI command
if ( defined( 'WP_CLI' ) && WP_CLI ) {
\WP_CLI::add_command( 'books', 'MyPlugin\CLI\Books_Command' );
}php
<?php
namespace MyPlugin\CLI;
class Books_Command {
/**
* List all books.
*
* ## OPTIONS
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - yaml
* ---
*
* [--status=<status>]
* : Filter by post status.
*
* ## EXAMPLES
*
* wp books list
* wp books list --format=json
* wp books list --status=publish
*
* @when after_wp_load
*/
public function list( $args, $assoc_args ) {
$defaults = [
'format' => 'table',
'status' => 'any',
];
$assoc_args = wp_parse_args( $assoc_args, $defaults );
$query_args = [
'post_type' => 'book',
'posts_per_page' => -1,
'post_status' => $assoc_args['status'],
];
$books = get_posts( $query_args );
if ( empty( $books ) ) {
\WP_CLI::warning( 'No books found.' );
return;
}
$items = [];
foreach ( $books as $book ) {
$items[] = [
'ID' => $book->ID,
'Title' => $book->post_title,
'Status' => $book->post_status,
'Author' => get_the_author_meta( 'display_name', $book->post_author ),
'Date' => get_the_date( 'Y-m-d H:i:s', $book ),
];
}
\WP_CLI\Utils\format_items( $assoc_args['format'], $items, [ 'ID', 'Title', 'Status', 'Author', 'Date' ] );
\WP_CLI::success( sprintf( 'Found %d books.', count( $items ) ) );
}
/**
* Create a new book.
*
* ## OPTIONS
*
* <title>
* : The book title.
*
* [--content=<content>]
* : The book content.
*
* [--status=<status>]
* : The book status.
* ---
* default: draft
* options:
* - draft
* - publish
* - private
* ---
*
* [--isbn=<isbn>]
* : The book ISBN.
*
* [--pages=<pages>]
* : Number of pages.
*
* ## EXAMPLES
*
* wp books create "My Book Title"
* wp books create "My Book" --status=publish --isbn=978-3-16-148410-0 --pages=350
*
* @when after_wp_load
*/
public function create( $args, $assoc_args ) {
$title = $args[0];
$defaults = [
'content' => '',
'status' => 'draft',
];
$assoc_args = wp_parse_args( $assoc_args, $defaults );
$post_data = [
'post_type' => 'book',
'post_title' => $title,
'post_content' => $assoc_args['content'],
'post_status' => $assoc_args['status'],
'post_author' => get_current_user_id(),
];
$book_id = wp_insert_post( $post_data, true );
if ( is_wp_error( $book_id ) ) {
\WP_CLI::error( 'Failed to create book: ' . $book_id->get_error_message() );
}
// Add custom metadata
if ( isset( $assoc_args['isbn'] ) ) {
update_post_meta( $book_id, '_isbn', sanitize_text_field( $assoc_args['isbn'] ) );
}
if ( isset( $assoc_args['pages'] ) ) {
update_post_meta( $book_id, '_pages', absint( $assoc_args['pages'] ) );
}
\WP_CLI::success( sprintf( 'Created book #%d: %s', $book_id, $title ) );
}
/**
* Import books from CSV file.
*
* ## OPTIONS
*
* <file>
* : Path to CSV file.
*
* [--dry-run]
* : Preview import without creating books.
*
* ## EXAMPLES
*
* wp books import books.csv
* wp books import books.csv --dry-run
*
* @when after_wp_load
*/
public function import( $args, $assoc_args ) {
$file = $args[0];
$dry_run = isset( $assoc_args['dry-run'] );
if ( ! file_exists( $file ) ) {
\WP_CLI::error( 'File not found: ' . $file );
}
$csv = array_map( 'str_getcsv', file( $file ) );
$header = array_shift( $csv );
$total = count( $csv );
$created = 0;
\WP_CLI::log( sprintf( 'Processing %d books...', $total ) );
$progress = \WP_CLI\Utils\make_progress_bar( 'Importing books', $total );
foreach ( $csv as $row ) {
$book = array_combine( $header, $row );
if ( $dry_run ) {
\WP_CLI::log( sprintf( 'Would create: %s', $book['title'] ) );
} else {
$post_data = [
'post_type' => 'book',
'post_title' => $book['title'],
'post_content' => $book['content'] ?? '',
'post_status' => $book['status'] ?? 'draft',
];
$book_id = wp_insert_post( $post_data, true );
if ( ! is_wp_error( $book_id ) ) {
if ( isset( $book['isbn'] ) ) {
update_post_meta( $book_id, '_isbn', $book['isbn'] );
}
$created++;
}
}
$progress->tick();
}
$progress->finish();
if ( $dry_run ) {
\WP_CLI::success( sprintf( 'Dry run complete. Would create %d books.', $total ) );
} else {
\WP_CLI::success( sprintf( 'Imported %d of %d books.', $created, $total ) );
}
}
/**
* Generate sample books.
*
* ## OPTIONS
*
* [--count=<count>]
* : Number of books to generate.
* ---
* default: 10
* ---
*
* [--status=<status>]
* : Book status.
* ---
* default: publish
* ---
*
* ## EXAMPLES
*
* wp books generate --count=50
* wp books generate --count=100 --status=draft
*
* @when after_wp_load
*/
public function generate( $args, $assoc_args ) {
$count = isset( $assoc_args['count'] ) ? absint( $assoc_args['count'] ) : 10;
$status = isset( $assoc_args['status'] ) ? $assoc_args['status'] : 'publish';
$progress = \WP_CLI\Utils\make_progress_bar( 'Generating books', $count );
for ( $i = 1; $i <= $count; $i++ ) {
$post_data = [
'post_type' => 'book',
'post_title' => sprintf( 'Sample Book %d', $i ),
'post_content' => sprintf( 'This is sample book number %d.', $i ),
'post_status' => $status,
];
$book_id = wp_insert_post( $post_data );
// Add random metadata
update_post_meta( $book_id, '_isbn', sprintf( '978-3-16-%06d-0', rand( 100000, 999999 ) ) );
update_post_meta( $book_id, '_pages', rand( 100, 500 ) );
$progress->tick();
}
$progress->finish();
\WP_CLI::success( sprintf( 'Generated %d books.', $count ) );
}
}
// Register WP-CLI command
if ( defined( 'WP_CLI' ) && WP_CLI ) {
\WP_CLI::add_command( 'books', 'MyPlugin\CLI\Books_Command' );
}Interactive Prompts and Confirmation
交互式提示与确认
php
/**
* Delete all books (with confirmation).
*
* ## OPTIONS
*
* [--yes]
* : Skip confirmation prompt.
*
* ## EXAMPLES
*
* wp books delete-all
* wp books delete-all --yes
*
* @when after_wp_load
*/
public function delete_all( $args, $assoc_args ) {
$books = get_posts([
'post_type' => 'book',
'posts_per_page' => -1,
'fields' => 'ids',
]);
$count = count( $books );
if ( 0 === $count ) {
\WP_CLI::warning( 'No books to delete.' );
return;
}
// Prompt for confirmation unless --yes flag is provided
\WP_CLI::confirm( sprintf( 'Are you sure you want to delete %d books?', $count ), $assoc_args );
$progress = \WP_CLI\Utils\make_progress_bar( 'Deleting books', $count );
foreach ( $books as $book_id ) {
wp_delete_post( $book_id, true ); // Force delete
$progress->tick();
}
$progress->finish();
\WP_CLI::success( sprintf( 'Deleted %d books.', $count ) );
}php
/**
* Delete all books (with confirmation).
*
* ## OPTIONS
*
* [--yes]
* : Skip confirmation prompt.
*
* ## EXAMPLES
*
* wp books delete-all
* wp books delete-all --yes
*
* @when after_wp_load
*/
public function delete_all( $args, $assoc_args ) {
$books = get_posts([
'post_type' => 'book',
'posts_per_page' => -1,
'fields' => 'ids',
]);
$count = count( $books );
if ( 0 === $count ) {
\WP_CLI::warning( 'No books to delete.' );
return;
}
// Prompt for confirmation unless --yes flag is provided
\WP_CLI::confirm( sprintf( 'Are you sure you want to delete %d books?', $count ), $assoc_args );
$progress = \WP_CLI\Utils\make_progress_bar( 'Deleting books', $count );
foreach ( $books as $book_id ) {
wp_delete_post( $book_id, true ); // Force delete
$progress->tick();
}
$progress->finish();
\WP_CLI::success( sprintf( 'Deleted %d books.', $count ) );
}Testing WP-CLI Commands
测试WP-CLI命令
bash
undefinedbash
undefinedList all WP-CLI commands
List all WP-CLI commands
wp cli command-list
wp cli command-list
Get help for custom command
Get help for custom command
wp help books
wp help books create
wp help books
wp help books create
Test commands
Test commands
wp books list
wp books list --format=json
wp books create "Test Book" --status=publish
wp books generate --count=50
wp books import books.csv --dry-run
wp books delete-all --yes
undefinedwp books list
wp books list --format=json
wp books create "Test Book" --status=publish
wp books generate --count=50
wp books import books.csv --dry-run
wp books delete-all --yes
undefined3. Performance Optimization
3. 性能优化
Transients API (Expiring Cache)
Transients API(过期缓存)
php
// Store data with expiration
function get_popular_books() {
$transient_key = 'popular_books';
// Try to get cached value
$popular_books = get_transient( $transient_key );
if ( false === $popular_books ) {
// Cache miss - fetch and store
$popular_books = new WP_Query([
'post_type' => 'book',
'posts_per_page' => 10,
'meta_key' => '_view_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
]);
// Cache for 1 hour (3600 seconds)
set_transient( $transient_key, $popular_books, HOUR_IN_SECONDS );
}
return $popular_books;
}
// Invalidate cache on post update
add_action( 'save_post_book', 'invalidate_books_cache' );
function invalidate_books_cache( $post_id ) {
delete_transient( 'popular_books' );
}
// Site-specific transients (multisite)
set_site_transient( 'network_data', $data, DAY_IN_SECONDS );
$data = get_site_transient( 'network_data' );
delete_site_transient( 'network_data' );
// Clear all transients (cleanup)
global $wpdb;
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '%_transient_%'" );php
// Store data with expiration
function get_popular_books() {
$transient_key = 'popular_books';
// Try to get cached value
$popular_books = get_transient( $transient_key );
if ( false === $popular_books ) {
// Cache miss - fetch and store
$popular_books = new WP_Query([
'post_type' => 'book',
'posts_per_page' => 10,
'meta_key' => '_view_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
]);
// Cache for 1 hour (3600 seconds)
set_transient( $transient_key, $popular_books, HOUR_IN_SECONDS );
}
return $popular_books;
}
// Invalidate cache on post update
add_action( 'save_post_book', 'invalidate_books_cache' );
function invalidate_books_cache( $post_id ) {
delete_transient( 'popular_books' );
}
// Site-specific transients (multisite)
set_site_transient( 'network_data', $data, DAY_IN_SECONDS );
$data = get_site_transient( 'network_data' );
delete_site_transient( 'network_data' );
// Clear all transients (cleanup)
global $wpdb;
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '%_transient_%'" );Object Caching (Redis, Memcached)
对象缓存(Redis、Memcached)
php
// WordPress Object Cache functions (wp_cache_*)
// Works with persistent cache (Redis/Memcached) if configured
// Add to cache
wp_cache_add( 'my_key', $data, 'my_group', 3600 );
// Get from cache
$data = wp_cache_get( 'my_key', 'my_group' );
if ( false === $data ) {
// Cache miss - fetch and cache
$data = expensive_database_query();
wp_cache_set( 'my_key', $data, 'my_group', 3600 );
}
// Delete from cache
wp_cache_delete( 'my_key', 'my_group' );
// Flush entire cache
wp_cache_flush();
// Example: Cache user data
function get_user_books( $user_id ) {
$cache_key = "user_{$user_id}_books";
$cache_group = 'user_books';
$books = wp_cache_get( $cache_key, $cache_group );
if ( false === $books ) {
$books = get_posts([
'post_type' => 'book',
'author' => $user_id,
'posts_per_page' => -1,
]);
wp_cache_set( $cache_key, $books, $cache_group, HOUR_IN_SECONDS );
}
return $books;
}
// Invalidate user cache on book update
add_action( 'save_post_book', 'invalidate_user_books_cache', 10, 2 );
function invalidate_user_books_cache( $post_id, $post ) {
wp_cache_delete( "user_{$post->post_author}_books", 'user_books' );
}php
// WordPress Object Cache functions (wp_cache_*)
// Works with persistent cache (Redis/Memcached) if configured
// Add to cache
wp_cache_add( 'my_key', $data, 'my_group', 3600 );
// Get from cache
$data = wp_cache_get( 'my_key', 'my_group' );
if ( false === $data ) {
// Cache miss - fetch and cache
$data = expensive_database_query();
wp_cache_set( 'my_key', $data, 'my_group', 3600 );
}
// Delete from cache
wp_cache_delete( 'my_key', 'my_group' );
// Flush entire cache
wp_cache_flush();
// Example: Cache user data
function get_user_books( $user_id ) {
$cache_key = "user_{$user_id}_books";
$cache_group = 'user_books';
$books = wp_cache_get( $cache_key, $cache_group );
if ( false === $books ) {
$books = get_posts([
'post_type' => 'book',
'author' => $user_id,
'posts_per_page' => -1,
]);
wp_cache_set( $cache_key, $books, $cache_group, HOUR_IN_SECONDS );
}
return $books;
}
// Invalidate user cache on book update
add_action( 'save_post_book', 'invalidate_user_books_cache', 10, 2 );
function invalidate_user_books_cache( $post_id, $post ) {
wp_cache_delete( "user_{$post->post_author}_books", 'user_books' );
}Redis Configuration (object-cache.php)
Redis配置(object-cache.php)
php
// Install Redis plugin or drop-in
// Recommended: https://wordpress.org/plugins/redis-cache/
// Or manual configuration:
// wp-content/object-cache.php
<?php
// Redis configuration
global $redis_server;
$redis_server = [
'host' => '127.0.0.1',
'port' => 6379,
'auth' => '', // Password if required
'database' => 0, // Redis database number
];
// wp-config.php settings
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );php
// Install Redis plugin or drop-in
// Recommended: https://wordpress.org/plugins/redis-cache/
// Or manual configuration:
// wp-content/object-cache.php
<?php
// Redis configuration
global $redis_server;
$redis_server = [
'host' => '127.0.0.1',
'port' => 6379,
'auth' => '', // Password if required
'database' => 0, // Redis database number
];
// wp-config.php settings
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );Database Query Optimization
数据库查询优化
php
// BAD: Multiple queries in loop
$posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]);
foreach ( $posts as $post ) {
$author = get_user_by( 'id', $post->post_author ); // N+1 query problem
$meta = get_post_meta( $post->ID, '_isbn', true ); // Another query per iteration
}
// GOOD: Pre-fetch data
$posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]);
$author_ids = wp_list_pluck( $posts, 'post_author' );
$authors = get_users([ 'include' => $author_ids ]); // Single query
$authors_by_id = [];
foreach ( $authors as $author ) {
$authors_by_id[ $author->ID ] = $author;
}
// Pre-load all meta with update_meta_cache()
update_post_caches( $posts, 'book' );
foreach ( $posts as $post ) {
$author = $authors_by_id[ $post->post_author ];
$isbn = get_post_meta( $post->ID, '_isbn', true ); // Uses cache
}
// Custom queries with JOIN
global $wpdb;
$results = $wpdb->get_results("
SELECT p.*, pm.meta_value as isbn
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_isbn'
WHERE p.post_type = 'book'
AND p.post_status = 'publish'
ORDER BY p.post_date DESC
LIMIT 10
");
// Add database indexes for frequently queried meta
global $wpdb;
$wpdb->query("
CREATE INDEX meta_key_value ON {$wpdb->postmeta} (meta_key, meta_value(20))
");php
// BAD: Multiple queries in loop
$posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]);
foreach ( $posts as $post ) {
$author = get_user_by( 'id', $post->post_author ); // N+1 query problem
$meta = get_post_meta( $post->ID, '_isbn', true ); // Another query per iteration
}
// GOOD: Pre-fetch data
$posts = get_posts([ 'post_type' => 'book', 'posts_per_page' => -1 ]);
$author_ids = wp_list_pluck( $posts, 'post_author' );
$authors = get_users([ 'include' => $author_ids ]); // Single query
$authors_by_id = [];
foreach ( $authors as $author ) {
$authors_by_id[ $author->ID ] = $author;
}
// Pre-load all meta with update_meta_cache()
update_post_caches( $posts, 'book' );
foreach ( $posts as $post ) {
$author = $authors_by_id[ $post->post_author ];
$isbn = get_post_meta( $post->ID, '_isbn', true ); // Uses cache
}
// Custom queries with JOIN
global $wpdb;
$results = $wpdb->get_results("
SELECT p.*, pm.meta_value as isbn
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_isbn'
WHERE p.post_type = 'book'
AND p.post_status = 'publish'
ORDER BY p.post_date DESC
LIMIT 10
");
// Add database indexes for frequently queried meta
global $wpdb;
$wpdb->query("
CREATE INDEX meta_key_value ON {$wpdb->postmeta} (meta_key, meta_value(20))
");Lazy Loading and Pagination
懒加载与分页
php
// Paginate large queries
function get_books_paginated( $page = 1, $per_page = 20 ) {
$args = [
'post_type' => 'book',
'posts_per_page' => $per_page,
'paged' => $page,
];
return new WP_Query( $args );
}
// Infinite scroll with AJAX
add_action( 'wp_ajax_load_more_books', 'ajax_load_more_books' );
add_action( 'wp_ajax_nopriv_load_more_books', 'ajax_load_more_books' );
function ajax_load_more_books() {
check_ajax_referer( 'load_more_nonce', 'nonce' );
$page = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
$query = get_books_paginated( $page, 10 );
if ( $query->have_posts() ) {
ob_start();
while ( $query->have_posts() ) {
$query->the_post();
get_template_part( 'template-parts/content', 'book' );
}
$html = ob_get_clean();
wp_send_json_success([
'html' => $html,
'has_more' => $query->max_num_pages > $page,
]);
} else {
wp_send_json_error( 'No more books' );
}
}
// Lazy load images (native HTML)
<img src="book-cover.jpg" loading="lazy" alt="Book Cover">php
// Paginate large queries
function get_books_paginated( $page = 1, $per_page = 20 ) {
$args = [
'post_type' => 'book',
'posts_per_page' => $per_page,
'paged' => $page,
];
return new WP_Query( $args );
}
// Infinite scroll with AJAX
add_action( 'wp_ajax_load_more_books', 'ajax_load_more_books' );
add_action( 'wp_ajax_nopriv_load_more_books', 'ajax_load_more_books' );
function ajax_load_more_books() {
check_ajax_referer( 'load_more_nonce', 'nonce' );
$page = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
$query = get_books_paginated( $page, 10 );
if ( $query->have_posts() ) {
ob_start();
while ( $query->have_posts() ) {
$query->the_post();
get_template_part( 'template-parts/content', 'book' );
}
$html = ob_get_clean();
wp_send_json_success([
'html' => $html,
'has_more' => $query->max_num_pages > $page,
]);
} else {
wp_send_json_error( 'No more books' );
}
}
// Lazy load images (native HTML)
<img src="book-cover.jpg" loading="lazy" alt="Book Cover">Profiling with Query Monitor
使用Query Monitor进行性能分析
php
// Install Query Monitor plugin
// https://wordpress.org/plugins/query-monitor/
// Add custom timing measurements
do_action( 'qm/start', 'my_expensive_operation' );
// ... expensive code ...
do_action( 'qm/stop', 'my_expensive_operation' );
// Log custom data
do_action( 'qm/debug', 'Custom debug message' );
do_action( 'qm/info', $data_to_inspect );
// Benchmark queries
Query Monitor shows:
// - SQL queries (count, time, duplicates)
// - HTTP requests
// - Hooks and actions
// - PHP errors and notices
// - Template file hierarchy
// - Enqueued scripts/stylesphp
// Install Query Monitor plugin
// https://wordpress.org/plugins/query-monitor/
// Add custom timing measurements
do_action( 'qm/start', 'my_expensive_operation' );
// ... expensive code ...
do_action( 'qm/stop', 'my_expensive_operation' );
// Log custom data
do_action( 'qm/debug', 'Custom debug message' );
do_action( 'qm/info', $data_to_inspect );
// Benchmark queries
Query Monitor shows:
// - SQL queries (count, time, duplicates)
// - HTTP requests
// - Hooks and actions
// - PHP errors and notices
// - Template file hierarchy
// - Enqueued scripts/styles4. Caching Strategies
4. 缓存策略
Fragment Caching
片段缓存
php
// Cache HTML fragments
function render_book_grid() {
$cache_key = 'book_grid_html';
$html = get_transient( $cache_key );
if ( false === $html ) {
ob_start();
$books = new WP_Query([
'post_type' => 'book',
'posts_per_page' => 12,
]);
if ( $books->have_posts() ) {
echo '<div class="book-grid">';
while ( $books->have_posts() ) {
$books->the_post();
?>
<div class="book-item">
<h3><?php the_title(); ?></h3>
<?php the_post_thumbnail( 'medium' ); ?>
</div>
<?php
}
echo '</div>';
}
wp_reset_postdata();
$html = ob_get_clean();
set_transient( $cache_key, $html, HOUR_IN_SECONDS );
}
echo $html;
}php
// Cache HTML fragments
function render_book_grid() {
$cache_key = 'book_grid_html';
$html = get_transient( $cache_key );
if ( false === $html ) {
ob_start();
$books = new WP_Query([
'post_type' => 'book',
'posts_per_page' => 12,
]);
if ( $books->have_posts() ) {
echo '<div class="book-grid">';
while ( $books->have_posts() ) {
$books->the_post();
?>
<div class="book-item">
<h3><?php the_title(); ?></h3>
<?php the_post_thumbnail( 'medium' ); ?>
</div>
<?php
}
echo '</div>';
}
wp_reset_postdata();
$html = ob_get_clean();
set_transient( $cache_key, $html, HOUR_IN_SECONDS );
}
echo $html;
}Page Caching vs Object Caching
页面缓存 vs 对象缓存
php
/**
* Page Caching:
* - Caches entire HTML pages
* - Fastest (serves static HTML)
* - Plugins: WP Super Cache, W3 Total Cache, WP Rocket
* - Bypassed for logged-in users
*
* Object Caching:
* - Caches database queries and computed values
* - Works for all users (logged-in and anonymous)
* - Requires persistent cache backend (Redis, Memcached)
* - Granular cache control
*/
// Example: Bypass page cache for dynamic content
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
// Cache-Control headers for static assets
function add_cache_headers() {
if ( is_admin() || is_user_logged_in() ) {
return;
}
// Cache static pages for 1 hour
if ( is_page() || is_single() ) {
header( 'Cache-Control: public, max-age=3600' );
}
// Cache archives for 30 minutes
if ( is_archive() || is_home() ) {
header( 'Cache-Control: public, max-age=1800' );
}
}
add_action( 'send_headers', 'add_cache_headers' );php
/**
* Page Caching:
* - Caches entire HTML pages
* - Fastest (serves static HTML)
* - Plugins: WP Super Cache, W3 Total Cache, WP Rocket
* - Bypassed for logged-in users
*
* Object Caching:
* - Caches database queries and computed values
* - Works for all users (logged-in and anonymous)
* - Requires persistent cache backend (Redis, Memcached)
* - Granular cache control
*/
// Example: Bypass page cache for dynamic content
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
// Cache-Control headers for static assets
function add_cache_headers() {
if ( is_admin() || is_user_logged_in() ) {
return;
}
// Cache static pages for 1 hour
if ( is_page() || is_single() ) {
header( 'Cache-Control: public, max-age=3600' );
}
// Cache archives for 30 minutes
if ( is_archive() || is_home() ) {
header( 'Cache-Control: public, max-age=1800' );
}
}
add_action( 'send_headers', 'add_cache_headers' );Cache Invalidation Patterns
缓存失效模式
php
// Pattern 1: Time-based expiration
set_transient( 'data', $value, 12 * HOUR_IN_SECONDS );
// Pattern 2: Event-based invalidation
add_action( 'save_post_book', 'clear_book_caches' );
function clear_book_caches( $post_id ) {
// Clear specific caches
delete_transient( 'popular_books' );
delete_transient( 'recent_books' );
wp_cache_delete( "book_{$post_id}", 'books' );
// Clear author cache
$post = get_post( $post_id );
wp_cache_delete( "user_{$post->post_author}_books", 'user_books' );
}
// Pattern 3: Versioned cache keys
function get_cache_version() {
$version = wp_cache_get( 'cache_version', 'global' );
if ( false === $version ) {
$version = time();
wp_cache_set( 'cache_version', $version, 'global' );
}
return $version;
}
function get_cached_data( $key ) {
$version = get_cache_version();
$versioned_key = "{$key}_v{$version}";
return wp_cache_get( $versioned_key, 'my_group' );
}
function set_cached_data( $key, $data ) {
$version = get_cache_version();
$versioned_key = "{$key}_v{$version}";
wp_cache_set( $versioned_key, $data, 'my_group', HOUR_IN_SECONDS );
}
function invalidate_all_caches() {
// Increment version to invalidate all caches
$new_version = time();
wp_cache_set( 'cache_version', $new_version, 'global' );
}
// Pattern 4: Cache warming
add_action( 'save_post_book', 'warm_book_caches', 20 );
function warm_book_caches( $post_id ) {
// Rebuild cache immediately after invalidation
get_popular_books(); // Rebuilds transient
}php
// Pattern 1: Time-based expiration
set_transient( 'data', $value, 12 * HOUR_IN_SECONDS );
// Pattern 2: Event-based invalidation
add_action( 'save_post_book', 'clear_book_caches' );
function clear_book_caches( $post_id ) {
// Clear specific caches
delete_transient( 'popular_books' );
delete_transient( 'recent_books' );
wp_cache_delete( "book_{$post_id}", 'books' );
// Clear author cache
$post = get_post( $post_id );
wp_cache_delete( "user_{$post->post_author}_books", 'user_books' );
}
// Pattern 3: Versioned cache keys
function get_cache_version() {
$version = wp_cache_get( 'cache_version', 'global' );
if ( false === $version ) {
$version = time();
wp_cache_set( 'cache_version', $version, 'global' );
}
return $version;
}
function get_cached_data( $key ) {
$version = get_cache_version();
$versioned_key = "{$key}_v{$version}";
return wp_cache_get( $versioned_key, 'my_group' );
}
function set_cached_data( $key, $data ) {
$version = get_cache_version();
$versioned_key = "{$key}_v{$version}";
wp_cache_set( $versioned_key, $data, 'my_group', HOUR_IN_SECONDS );
}
function invalidate_all_caches() {
// Increment version to invalidate all caches
$new_version = time();
wp_cache_set( 'cache_version', $new_version, 'global' );
}
// Pattern 4: Cache warming
add_action( 'save_post_book', 'warm_book_caches', 20 );
function warm_book_caches( $post_id ) {
// Rebuild cache immediately after invalidation
get_popular_books(); // Rebuilds transient
}CDN Integration
CDN集成
php
// Rewrite asset URLs to CDN
add_filter( 'wp_get_attachment_url', 'cdn_rewrite_url' );
add_filter( 'wp_calculate_image_srcset', 'cdn_rewrite_srcset' );
function cdn_rewrite_url( $url ) {
$cdn_domain = 'https://cdn.example.com';
$site_url = site_url();
// Only rewrite uploads directory
if ( strpos( $url, '/wp-content/uploads/' ) !== false ) {
return str_replace( $site_url, $cdn_domain, $url );
}
return $url;
}
function cdn_rewrite_srcset( $sources ) {
if ( ! is_array( $sources ) ) {
return $sources;
}
foreach ( $sources as &$source ) {
$source['url'] = cdn_rewrite_url( $source['url'] );
}
return $sources;
}
// Purge CDN cache on content update
add_action( 'save_post', 'purge_cdn_cache' );
function purge_cdn_cache( $post_id ) {
// Example: Cloudflare API
$zone_id = 'your_zone_id';
$api_key = 'your_api_key';
$email = 'your@email.com';
$url = get_permalink( $post_id );
wp_remote_post( "https://api.cloudflare.com/client/v4/zones/{$zone_id}/purge_cache", [
'headers' => [
'X-Auth-Email' => $email,
'X-Auth-Key' => $api_key,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'files' => [ $url ],
]),
]);
}php
// Rewrite asset URLs to CDN
add_filter( 'wp_get_attachment_url', 'cdn_rewrite_url' );
add_filter( 'wp_calculate_image_srcset', 'cdn_rewrite_srcset' );
function cdn_rewrite_url( $url ) {
$cdn_domain = 'https://cdn.example.com';
$site_url = site_url();
// Only rewrite uploads directory
if ( strpos( $url, '/wp-content/uploads/' ) !== false ) {
return str_replace( $site_url, $cdn_domain, $url );
}
return $url;
}
function cdn_rewrite_srcset( $sources ) {
if ( ! is_array( $sources ) ) {
return $sources;
}
foreach ( $sources as &$source ) {
$source['url'] = cdn_rewrite_url( $source['url'] );
}
return $sources;
}
// Purge CDN cache on content update
add_action( 'save_post', 'purge_cdn_cache' );
function purge_cdn_cache( $post_id ) {
// Example: Cloudflare API
$zone_id = 'your_zone_id';
$api_key = 'your_api_key';
$email = 'your@email.com';
$url = get_permalink( $post_id );
wp_remote_post( "https://api.cloudflare.com/client/v4/zones/{$zone_id}/purge_cache", [
'headers' => [
'X-Auth-Email' => $email,
'X-Auth-Key' => $api_key,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'files' => [ $url ],
]),
]);
}5. Advanced Database Patterns
5. 高级数据库模式
WP_Query Optimization
WP_Query优化
php
// Use 'fields' parameter to limit data
$query = new WP_Query([
'post_type' => 'book',
'fields' => 'ids', // Only return IDs (faster)
]);
// Count queries
$count = new WP_Query([
'post_type' => 'book',
'posts_per_page' => -1,
'fields' => 'ids',
'no_found_rows' => true, // Skip SQL_CALC_FOUND_ROWS
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]);
// Complex meta queries with performance in mind
$query = new WP_Query([
'post_type' => 'book',
'meta_query' => [
'relation' => 'AND',
[
'key' => '_pages',
'value' => 200,
'compare' => '>',
'type' => 'NUMERIC',
],
[
'key' => '_rating',
'value' => 4,
'compare' => '>=',
'type' => 'DECIMAL',
],
],
'orderby' => 'meta_value_num',
'order' => 'DESC',
]);php
// Use 'fields' parameter to limit data
$query = new WP_Query([
'post_type' => 'book',
'fields' => 'ids', // Only return IDs (faster)
]);
// Count queries
$count = new WP_Query([
'post_type' => 'book',
'posts_per_page' => -1,
'fields' => 'ids',
'no_found_rows' => true, // Skip SQL_CALC_FOUND_ROWS
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]);
// Complex meta queries with performance in mind
$query = new WP_Query([
'post_type' => 'book',
'meta_query' => [
'relation' => 'AND',
[
'key' => '_pages',
'value' => 200,
'compare' => '>',
'type' => 'NUMERIC',
],
[
'key' => '_rating',
'value' => 4,
'compare' => '>=',
'type' => 'DECIMAL',
],
],
'orderby' => 'meta_value_num',
'order' => 'DESC',
]);Direct SQL for Complex Queries
复杂查询使用直接SQL
php
// When WP_Query is too slow, use direct SQL
global $wpdb;
// Get books with JOIN on multiple meta keys
$results = $wpdb->get_results("
SELECT
p.ID,
p.post_title,
pm1.meta_value as isbn,
pm2.meta_value as pages,
pm3.meta_value as rating
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_isbn'
LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_pages'
LEFT JOIN {$wpdb->postmeta} pm3 ON p.ID = pm3.post_id AND pm3.meta_key = '_rating'
WHERE p.post_type = 'book'
AND p.post_status = 'publish'
AND CAST(pm2.meta_value AS UNSIGNED) > 200
ORDER BY CAST(pm3.meta_value AS DECIMAL(3,2)) DESC
LIMIT 20
");
// Use $wpdb->prepare() for dynamic values
$min_pages = 200;
$results = $wpdb->get_results( $wpdb->prepare("
SELECT p.ID, p.post_title, pm.meta_value as pages
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = 'book'
AND pm.meta_key = '_pages'
AND CAST(pm.meta_value AS UNSIGNED) > %d
ORDER BY p.post_date DESC
", $min_pages ) );php
// When WP_Query is too slow, use direct SQL
global $wpdb;
// Get books with JOIN on multiple meta keys
$results = $wpdb->get_results("
SELECT
p.ID,
p.post_title,
pm1.meta_value as isbn,
pm2.meta_value as pages,
pm3.meta_value as rating
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_isbn'
LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_pages'
LEFT JOIN {$wpdb->postmeta} pm3 ON p.ID = pm3.post_id AND pm3.meta_key = '_rating'
WHERE p.post_type = 'book'
AND p.post_status = 'publish'
AND CAST(pm2.meta_value AS UNSIGNED) > 200
ORDER BY CAST(pm3.meta_value AS DECIMAL(3,2)) DESC
LIMIT 20
");
// Use $wpdb->prepare() for dynamic values
$min_pages = 200;
$results = $wpdb->get_results( $wpdb->prepare("
SELECT p.ID, p.post_title, pm.meta_value as pages
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = 'book'
AND pm.meta_key = '_pages'
AND CAST(pm.meta_value AS UNSIGNED) > %d
ORDER BY p.post_date DESC
", $min_pages ) );Database Indexing
数据库索引
php
// Add custom indexes in plugin activation
register_activation_hook( __FILE__, 'create_custom_indexes' );
function create_custom_indexes() {
global $wpdb;
// Index on post_type and post_status (common filter)
$wpdb->query("
CREATE INDEX idx_post_type_status
ON {$wpdb->posts} (post_type, post_status)
");
// Index on meta_key and meta_value for frequent lookups
$wpdb->query("
CREATE INDEX idx_meta_key_value
ON {$wpdb->postmeta} (meta_key, meta_value(191))
");
// Composite index for date-based queries
$wpdb->query("
CREATE INDEX idx_post_type_date
ON {$wpdb->posts} (post_type, post_date)
");
}
// Remove indexes on deactivation
register_deactivation_hook( __FILE__, 'remove_custom_indexes' );
function remove_custom_indexes() {
global $wpdb;
$wpdb->query( "DROP INDEX idx_post_type_status ON {$wpdb->posts}" );
$wpdb->query( "DROP INDEX idx_meta_key_value ON {$wpdb->postmeta}" );
$wpdb->query( "DROP INDEX idx_post_type_date ON {$wpdb->posts}" );
}php
// Add custom indexes in plugin activation
register_activation_hook( __FILE__, 'create_custom_indexes' );
function create_custom_indexes() {
global $wpdb;
// Index on post_type and post_status (common filter)
$wpdb->query("
CREATE INDEX idx_post_type_status
ON {$wpdb->posts} (post_type, post_status)
");
// Index on meta_key and meta_value for frequent lookups
$wpdb->query("
CREATE INDEX idx_meta_key_value
ON {$wpdb->postmeta} (meta_key, meta_value(191))
");
// Composite index for date-based queries
$wpdb->query("
CREATE INDEX idx_post_type_date
ON {$wpdb->posts} (post_type, post_date)
");
}
// Remove indexes on deactivation
register_deactivation_hook( __FILE__, 'remove_custom_indexes' );
function remove_custom_indexes() {
global $wpdb;
$wpdb->query( "DROP INDEX idx_post_type_status ON {$wpdb->posts}" );
$wpdb->query( "DROP INDEX idx_meta_key_value ON {$wpdb->postmeta}" );
$wpdb->query( "DROP INDEX idx_post_type_date ON {$wpdb->posts}" );
}6. Multisite Development
6. 多站点开发
Network-Activated Plugins
网络激活插件
php
// Detect multisite
if ( is_multisite() ) {
// Multisite-specific code
}
// Network-wide hooks
add_action( 'network_admin_menu', 'add_network_admin_page' );
function add_network_admin_page() {
add_menu_page(
'Network Settings',
'My Plugin',
'manage_network_options',
'my-network-settings',
'render_network_settings_page'
);
}
// Network options (not site-specific)
add_site_option( 'my_network_setting', 'value' );
$value = get_site_option( 'my_network_setting' );
update_site_option( 'my_network_setting', 'new_value' );
delete_site_option( 'my_network_setting' );php
// Detect multisite
if ( is_multisite() ) {
// Multisite-specific code
}
// Network-wide hooks
add_action( 'network_admin_menu', 'add_network_admin_page' );
function add_network_admin_page() {
add_menu_page(
'Network Settings',
'My Plugin',
'manage_network_options',
'my-network-settings',
'render_network_settings_page'
);
}
// Network options (not site-specific)
add_site_option( 'my_network_setting', 'value' );
$value = get_site_option( 'my_network_setting' );
update_site_option( 'my_network_setting', 'new_value' );
delete_site_option( 'my_network_setting' );Cross-Site Operations
跨站点操作
php
// Switch to another site
$current_blog_id = get_current_blog_id();
switch_to_blog( 2 ); // Switch to site ID 2
// Perform operations on site 2
$posts = get_posts([ 'post_type' => 'book' ]);
update_option( 'my_option', 'value' );
// Always restore
restore_current_blog();
// Iterate all sites
$sites = get_sites([ 'number' => 999 ]);
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
// Do something on each site
$count = wp_count_posts( 'book' );
error_log( "Site {$site->blog_id} has {$count->publish} books" );
restore_current_blog();
}php
// Switch to another site
$current_blog_id = get_current_blog_id();
switch_to_blog( 2 ); // Switch to site ID 2
// Perform operations on site 2
$posts = get_posts([ 'post_type' => 'book' ]);
update_option( 'my_option', 'value' );
// Always restore
restore_current_blog();
// Iterate all sites
$sites = get_sites([ 'number' => 999 ]);
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
// Do something on each site
$count = wp_count_posts( 'book' );
error_log( "Site {$site->blog_id} has {$count->publish} books" );
restore_current_blog();
}7. Best Practices
7. 最佳实践
Service-Oriented Architecture
面向服务架构
php
<?php
namespace MyPlugin\Services;
class BookService {
private $cache;
private $validator;
public function __construct( CacheService $cache, ValidationService $validator ) {
$this->cache = $cache;
$this->validator = $validator;
}
public function get_book( $book_id ) {
// Check cache first
$cache_key = "book_{$book_id}";
$book = $this->cache->get( $cache_key );
if ( false === $book ) {
$book = get_post( $book_id );
if ( $book && 'book' === $book->post_type ) {
$this->cache->set( $cache_key, $book, HOUR_IN_SECONDS );
}
}
return $book;
}
public function create_book( $data ) {
// Validate input
$errors = $this->validator->validate_book_data( $data );
if ( ! empty( $errors ) ) {
return new \WP_Error( 'validation_failed', 'Validation failed', $errors );
}
// Create book
$book_id = wp_insert_post([
'post_type' => 'book',
'post_title' => $data['title'],
'post_content' => $data['content'],
'post_status' => $data['status'],
]);
if ( is_wp_error( $book_id ) ) {
return $book_id;
}
// Clear related caches
$this->cache->invalidate_group( 'books' );
return $book_id;
}
}
// Dependency injection container
class Container {
private $services = [];
public function register( $name, $service ) {
$this->services[ $name ] = $service;
}
public function get( $name ) {
if ( ! isset( $this->services[ $name ] ) ) {
throw new \Exception( "Service not found: {$name}" );
}
return $this->services[ $name ];
}
}
// Bootstrap
$container = new Container();
$container->register( 'cache', new CacheService() );
$container->register( 'validator', new ValidationService() );
$container->register( 'books', new BookService(
$container->get( 'cache' ),
$container->get( 'validator' )
) );php
<?php
namespace MyPlugin\Services;
class BookService {
private $cache;
private $validator;
public function __construct( CacheService $cache, ValidationService $validator ) {
$this->cache = $cache;
$this->validator = $validator;
}
public function get_book( $book_id ) {
// Check cache first
$cache_key = "book_{$book_id}";
$book = $this->cache->get( $cache_key );
if ( false === $book ) {
$book = get_post( $book_id );
if ( $book && 'book' === $book->post_type ) {
$this->cache->set( $cache_key, $book, HOUR_IN_SECONDS );
}
}
return $book;
}
public function create_book( $data ) {
// Validate input
$errors = $this->validator->validate_book_data( $data );
if ( ! empty( $errors ) ) {
return new \WP_Error( 'validation_failed', 'Validation failed', $errors );
}
// Create book
$book_id = wp_insert_post([
'post_type' => 'book',
'post_title' => $data['title'],
'post_content' => $data['content'],
'post_status' => $data['status'],
]);
if ( is_wp_error( $book_id ) ) {
return $book_id;
}
// Clear related caches
$this->cache->invalidate_group( 'books' );
return $book_id;
}
}
// Dependency injection container
class Container {
private $services = [];
public function register( $name, $service ) {
$this->services[ $name ] = $service;
}
public function get( $name ) {
if ( ! isset( $this->services[ $name ] ) ) {
throw new \Exception( "Service not found: {$name}" );
}
return $this->services[ $name ];
}
}
// Bootstrap
$container = new Container();
$container->register( 'cache', new CacheService() );
$container->register( 'validator', new ValidationService() );
$container->register( 'books', new BookService(
$container->get( 'cache' ),
$container->get( 'validator' )
) );Event-Driven Design
事件驱动设计
php
// Fire custom events
do_action( 'myplugin_book_created', $book_id, $book_data );
do_action( 'myplugin_book_updated', $book_id, $old_data, $new_data );
do_action( 'myplugin_book_deleted', $book_id );
// Other plugins can listen
add_action( 'myplugin_book_created', function( $book_id, $book_data ) {
// Send email notification
// Update analytics
// Sync to external service
}, 10, 2 );
// Use filters for modifiable data
$book_data = apply_filters( 'myplugin_before_book_save', $book_data, $book_id );
$notification_recipients = apply_filters( 'myplugin_notification_recipients', [ 'admin@example.com' ], $book_id );php
// Fire custom events
do_action( 'myplugin_book_created', $book_id, $book_data );
do_action( 'myplugin_book_updated', $book_id, $old_data, $new_data );
do_action( 'myplugin_book_deleted', $book_id );
// Other plugins can listen
add_action( 'myplugin_book_created', function( $book_id, $book_data ) {
// Send email notification
// Update analytics
// Sync to external service
}, 10, 2 );
// Use filters for modifiable data
$book_data = apply_filters( 'myplugin_before_book_save', $book_data, $book_id );
$notification_recipients = apply_filters( 'myplugin_notification_recipients', [ 'admin@example.com' ], $book_id );Scalability Considerations
可扩展性考量
php
/**
* Performance Checklist:
*
* 1. Database:
* - Use indexes on frequently queried columns
* - Avoid SELECT * queries (use specific fields)
* - Batch operations instead of loops
* - Use LIMIT for large datasets
*
* 2. Caching:
* - Implement object caching (Redis/Memcached)
* - Use transients for expensive operations
* - Fragment caching for HTML blocks
* - CDN for static assets
*
* 3. Queries:
* - Use 'fields' => 'ids' when possible
* - Set 'no_found_rows' => true if not paginating
* - Disable update_post_meta_cache and update_post_term_cache when not needed
*
* 4. Assets:
* - Minify and concatenate CSS/JS
* - Lazy load images
* - Use responsive images (srcset)
* - Defer non-critical JavaScript
*
* 5. Monitoring:
* - Use Query Monitor for development
* - New Relic or Application Insights for production
* - Monitor slow queries
* - Track cache hit rates
*/
// Example: Batch processing with WP-CLI
function process_books_batch() {
$offset = 0;
$batch_size = 100;
do {
$books = get_posts([
'post_type' => 'book',
'posts_per_page' => $batch_size,
'offset' => $offset,
'fields' => 'ids',
]);
foreach ( $books as $book_id ) {
// Process each book
update_post_meta( $book_id, '_processed', time() );
}
$offset += $batch_size;
// Prevent memory leaks
wp_cache_flush();
} while ( count( $books ) === $batch_size );
}php
/**
* Performance Checklist:
*
* 1. Database:
* - Use indexes on frequently queried columns
* - Avoid SELECT * queries (use specific fields)
* - Batch operations instead of loops
* - Use LIMIT for large datasets
*
* 2. Caching:
* - Implement object caching (Redis/Memcached)
* - Use transients for expensive operations
* - Fragment caching for HTML blocks
* - CDN for static assets
*
* 3. Queries:
* - Use 'fields' => 'ids' when possible
* - Set 'no_found_rows' => true if not paginating
* - Disable update_post_meta_cache and update_post_term_cache when not needed
*
* 4. Assets:
* - Minify and concatenate CSS/JS
* - Lazy load images
* - Use responsive images (srcset)
* - Defer non-critical JavaScript
*
* 5. Monitoring:
* - Use Query Monitor for development
* - New Relic or Application Insights for production
* - Monitor slow queries
* - Track cache hit rates
*/
// Example: Batch processing with WP-CLI
function process_books_batch() {
$offset = 0;
$batch_size = 100;
do {
$books = get_posts([
'post_type' => 'book',
'posts_per_page' => $batch_size,
'offset' => $offset,
'fields' => 'ids',
]);
foreach ( $books as $book_id ) {
// Process each book
update_post_meta( $book_id, '_processed', time() );
}
$offset += $batch_size;
// Prevent memory leaks
wp_cache_flush();
} while ( count( $books ) === $batch_size );
}Related Skills
相关技能
When building advanced WordPress applications, consider these complementary skills (available in the skill library):
- WordPress Plugin Fundamentals: Core plugin architecture and hooks - essential foundation for custom REST endpoints and WP-CLI commands
- WordPress Security & Data Validation: Security best practices - critical for securing REST API endpoints and validating user input
- WordPress Testing & QA: Testing REST endpoints and WP-CLI - comprehensive testing strategies for advanced WordPress features
- GraphQL: Alternative to REST API - consider GraphQL as a modern alternative to WordPress REST API for complex data queries
- Docker: Development environment setup - containerize WordPress development for consistent and reproducible environments
在构建高级WordPress应用时,可考虑以下互补技能(技能库中提供):
- WordPress Plugin Fundamentals:核心插件架构与钩子 - 是自定义REST端点和WP-CLI命令的必备基础
- WordPress Security & Data Validation:安全最佳实践 - 对保护REST API端点和验证用户输入至关重要
- WordPress Testing & QA:测试REST端点和WP-CLI - 针对高级WordPress功能的全面测试策略
- GraphQL:REST API的替代方案 - 对于复杂数据查询,可考虑将GraphQL作为WordPress REST API的现代替代方案
- Docker:开发环境搭建 - 容器化WordPress开发环境,实现一致且可复现的开发体验
References
参考资料
- REST API Handbook - Official REST API documentation
- WP-CLI - Command-line interface for WordPress
- Object Cache - WordPress object caching
- Transients API - WordPress transients documentation
- Query Monitor - Performance profiling plugin
- Redis Object Cache - Redis integration plugin
- REST API Handbook - 官方REST API文档
- WP-CLI - WordPress命令行界面
- Object Cache - WordPress对象缓存
- Transients API - WordPress Transients文档
- Query Monitor - 性能分析插件
- Redis Object Cache - Redis集成插件