К основному содержанию
скилл CX Codex

wordpress-advanced-architecture — скилл для Claude Code: REST API

Скилл по продвинутой разработке под WordPress: свои REST-маршруты, команды WP-CLI, кэширование транзиентами и объектным кэшем.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add bobmatnyc/claude-mpm-skills --skill wordpress-advanced-architecture -a claude-code
Открыть на GitHub

Что делает wordpress-advanced-architecture

Скилл открывается, когда вы пишете код плагина или темы под WordPress и хотите, чтобы агент держал в голове правильные паттерны. Внутри — регистрация кастомных REST-маршрутов с неймспейсом вида myplugin/v1, колбэки валидации и санитизации аргументов, проверки прав через permission_callback, полный CRUD с WP_Query и пагинацией. Ответы собираются через rest_ensure_response, к ним добавляются HATEOAS-ссылки self, prev и next.

Вторая часть — автоматизация через WP-CLI и производительность: кэширование транзиентами и объектным кэшем. Это заметно, когда проект растёт и запросы к базе начинают тормозить выдачу. Скилл помечен как user-invocable: false и disable-model-invocation: true, то есть вызывается не вручную, а подключается по контексту задачи.

Ограничение простое: скилл про WordPress на PHP, и без установленного WP-CLI команды автоматизации вы не запустите. Если ваш проект на другом стеке, брать нечего.

Какие задачи решает

  • зарегистрировать кастомный REST-маршрут с проверкой прав
  • собрать CRUD-эндпоинт с пагинацией и ссылками на следующую страницу
  • написать команду WP-CLI для рутинной задачи
  • закэшировать тяжёлые запросы транзиентами
  • добавить валидацию и санитизацию аргументов запроса

Примеры запросов агенту

  • зарегистрируй REST-маршрут myplugin/v1/books с пагинацией и permission_callback
  • сделай CRUD для типа записи book через REST API
  • добавь кэширование списка книг транзиентами

Как установить wordpress-advanced-architecture

1Через Skills CLI (Vercel)

npx skills add bobmatnyc/claude-mpm-skills --skill wordpress-advanced-architecture -a claude-code

2Вручную: положить файлы

~/.claude/skills/wordpress-advanced-architecture/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/wordpress-advanced-architecture/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора65

typescript-core Скилл с современными паттернами TypeScript: строгий tsconfig, валидация данных во время выполнения, продвинутые типы. Пригодится, когда проект на TS растёт и типы перестают спасать. CX Codex +1 475установок celery Скилл по Celery: фоновые задачи, периодические джобы и цепочки воркеров на Python. Пригодится, когда длинные операции надо унести из веб-запроса. CX Codex +1 454установок golang-testing-strategies Скилл для написания тестов на Go: табличные тесты, testify, gomock, бенчмарки и покрытие в CI. Пригодится, когда нужно навести порядок в тестовом наборе проекта. CX Codex +1 443установок sveltekit Скилл подсказывает агенту, как устроен SvelteKit: файловый роутинг, load-функции, form actions и адаптеры для деплоя. CX Codex +1 441установок digitalocean-management Скилл для Claude, Codex и Cursor, который помогает настроить мониторинг, проверки доступности и проекты в DigitalOcean. Пригодится, когда нужно собрать наблюдаемость и алерты по дроплетам и кластерам. CX Codex +1 438установок flask Скилл подкладывает агенту готовые шаблоны Flask: фабрика приложения, блюпринты, обработка запросов и ответов. Пригодится, когда пишешь REST API или микросервис на Python. CX Codex +1 437установок hono-rpc Скилл объясняет, как связать сервер и клиент Hono через типы TypeScript: экспорт AppType и клиент hc без кодогенерации. CX Codex +1 437установок hono-middleware Скилл с паттернами middleware для Hono: создание, композиция, встроенные модули и порядок выполнения. Пригодится, когда в приложение нужно добавить авторизацию, CORS или логирование. CX Codex +1 431установок

Похожие

wordpress-pro https://github.com/Jeffallan Скилл помогает разрабатывать темы и плагины WordPress, блоки Gutenberg и магазины WooCommerce. Нужен для задач с REST API, хуками, безопасностью и производительностью. CX Codex +1 9 700установок wordpress-router WordPress Скилл определяет тип кодовой базы WordPress и направляет агента по нужным веткам разработки. Пригодится на старте задач с темами, плагинами или ядром. CX Codex +1 5 600установок wordpress-setup jezweb Скилл проверяет WP-CLI, тестирует SSH или REST API, настраивает авторизацию и сохраняет конфиг подключения к WordPress. CX Codex +1 1 500установок wordpress-performance-best-practices bartekmis Скилл для Claude и других ИИ-редакторов: 34 правила производительности WordPress, разбитые по приоритету. Пригодится при написании и ревью PHP-кода плагинов и тем. CX Codex +1 708установок wp-abilities-api WordPress Скилл для работы с WordPress Abilities API: регистрация способностей и категорий в PHP, выдача в REST, потребление в JS. Пригодится, когда клиент не видит способность или REST отдаёт пусто. CX Codex +1 4 300установок wordpress-elementor jezweb Редактирует страницы Elementor и управляет шаблонами WordPress через WP-CLI или автоматизацию браузера. Пригодится для быстрой замены текстов и визуальной правки макетов. CX Codex +1 2 900установок

Рядом в теме «Бэкенд и API»

Всё по теме «Бэкенд и API» →

Вопросы о wordpress-advanced-architecture

Как установить wordpress-advanced-architecture в Claude?

Выполните npx skills add bobmatnyc/claude-mpm-skills --skill wordpress-advanced-architecture -a claude-code. После этого файл скилла окажется в ~/.claude/skills/wordpress-advanced-architecture/SKILL.md.

Что делает wordpress-advanced-architecture?

Даёт агенту паттерны продвинутой разработки под WordPress: кастомные REST-эндпоинты, команды WP-CLI, оптимизацию производительности и кэширование.

Для каких платформ подходит скилл?

Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI и OpenCode. Для каждой платформы в репозитории есть своя команда установки.

Исходный текст

SKILL.md · MIT
---
name: wordpress-advanced-architecture
description: Advanced WordPress development with REST API endpoints, WP-CLI commands, performance optimization, and caching strategies for scalable applications.
user-invocable: false
disable-model-invocation: true
progressive_disclosure:
  entry_point:
    summary: "Advanced WordPress development with REST API, WP-CLI, performance optimization, and caching strategies"
    when_to_use:
      - "Building custom REST API endpoints"
      - "Automating WordPress tasks with WP-CLI"
      - "Optimizing performance with caching and transients"
    quick_start:
      - "Register custom REST endpoints with permissions"
      - "Create WP-CLI commands for automation"
      - "Implement caching with transients and object cache"
---

# Advanced WordPress Architecture

Master advanced WordPress development patterns including REST API endpoints, WP-CLI commands, performance optimization, and caching strategies for scalable WordPress applications.

## 1. REST API Development

The WordPress REST API provides a powerful interface for creating custom endpoints with proper authentication, validation, and response formatting.

### 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',
            ],
        ],
    ]);
}
```

### Complete CRUD Implementation

```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 API Authentication

```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

WP-CLI enables automation of WordPress tasks through custom commands.

### 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' );
}
```

### 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 ) );
}
```

### Testing WP-CLI Commands

```bash
# List all WP-CLI commands
wp cli command-list

# Get help for custom command
wp help books
wp help books create

# 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
```

## 3. Performance Optimization

### Transients API (Expiring Cache)

```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)

```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)

```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 );
```...

© bobmatnyc · MIT · bobmatnyc/claude-mpm-skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой