| 开发者 | 301st |
|---|---|
| 更新时间 | 2026年5月15日 01:42 |
| PHP版本: | 8.0 及以上 |
| WordPress版本: | 6.9 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
{a|b|c} — randomly pick one option, with nesting support[<config>a|b|c] — pick N elements, shuffle, join with custom separators%var% — global, local (#set), and shortcode-level variable scopes{?VAR?then|else} — render a branch based on whether a variable is set (also {?!VAR?then} inverted){plural <count>: form1|form2|form3} — pick grammatically correct noun form by count. RU/UK/BE 3-form (one|few|many), EN-style 2-form (one|many). First spintax engine with first-class plurals.#include or [spintax]wp spintax bindings list|apply|test|export|importspintax folder to /wp-content/plugins/[spintax slug="my-template"] in posts/pages or spintax_render('my-template') in theme fileswp spintax bindings apply --binding=<id> --all. Action Scheduler turns those into one-click chunked async jobs that don't block the request. If you already use WooCommerce or another plugin that bundles Action Scheduler, you're already set; the Bindings page only shows the install notice when AS isn't loaded.Go to Spintax > Add New in the WordPress admin. Enter a title and your spintax markup in the editor.
{a|b|c} — randomly picks one option[a|b|c] — permutation: picks N elements, shuffles, joins with space[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> a|b|c|d] — configured permutation%variable% — variable reference#set %var% = value — local variable definition{?VAR?then|else} — conditional: render a branch by truthiness of %VAR% (also {?!VAR?then} inverted){plural %Count%: form1|form2|form3} — plural agreement: picks the correct grammatical form by count (RU 3-form, EN 2-form)/#comment#/ — block comment (stripped from output)#include "slug" — embed another template{plural N: form1|form2|form3} in depth (EN/RU){?VAR?then|else} value-driven branching (EN/RU)The plugin uses the WordPress Object Cache API. With a persistent backend (Redis, Memcached), cached output persists across requests. Without one, templates are re-rendered on each page load.
Yes: [spintax slug="greeting" name="Alice" city="Moscow"] makes %name% and %city% available inside the template.
A binding pairs a Spintax template (or a per-post inline source) with one target field on one post type — for example "Posts → ACF: hero_subtitle". Configure it once under Spintax → Bindings and the plugin populates the field on every matching post on save, on a cron schedule, or on demand via Bulk Apply. Manual edits are preserved by default (hash-tracked); flags control whether the binding auto-seeds empty fields, regenerates on every save, or clears the field when the template renders to empty.
Yes. Bindings support both ACF (text / textarea / wysiwyg, top-level fields) and plain post-meta keys. ACF Free and Pro are both supported; nested fields (repeater / flexible_content rows) are not supported in 2.0 — that lands in a later release. The form-side field picker auto-fills the stable ACF field key so writes work on the first save without ACF's reference-meta handshake.
It's a recommended optional dependency for binding-heavy sites. The plugin works without it: admins can run a walk via the synchronous Run now button on each binding card, or wp spintax bindings apply --binding=<id> --all from the CLI. What Action Scheduler adds is chunked async execution, so:
Both walk every matching post for a binding and produce the same writes. They differ in how the walk runs:
Spintax → Logs in the admin sidebar. Both paths log a completion entry per walk (e.g. Bulk Apply run_synchronously completed for binding <id> — wrote=N skipped=M cleared=K.), plus warnings for partial failures. The Logs page supports level filtering, substring search, and pagination; entries are kept in a ring buffer sized by Settings → Spintax → Max log entries.
Five subcommands under wp spintax bindings:
wp spintax bindings list [--format=table|json|csv] — list all bindings on the site.wp spintax bindings apply --binding=<id> [--all|--post=<id>] — run a binding against every matching post (--all) or a single post (--post=<id>). This is the synchronous fallback path for Bulk Apply.wp spintax bindings test --binding=<id> --post=<id> — dry-run a binding against one post and report what would be written (target value, rendered preview, skip reason). Same logic as the admin Test panel; use this instead of apply when you want a preview.wp spintax bindings export {--binding=<id>|--all} [> bindings.json] — emit one binding or the full store as JSON to stdout, deduped by (post_type, target.key).wp spintax bindings import --file=bindings.json [--overwrite] [--dry-run] — import bindings from JSON. --overwrite updates matches on the same target triple; without it, duplicates are skipped. Use --dry-run to preview the plan without writing.A binding template sees four layered variable sources (later layers override earlier ones):
#set block in Settings → Spintax. Site-wide.#set block in the binding's Variables tab. Applies to that binding only.%post_id%, %post_title%, %post_url%, %post_slug%, %post_date%, %post_modified%, %author_id%, %author_name%.%acf_<field_name>%. Siblings are always fresh on save: the binding runs after ACF persists.{a|b|c}, [a|b], {?VAR?then|else}, {plural %N%: …}, #include "slug", /#comment#/).
Two trigger paths, both configurable per binding under "Triggers":
Each binding signs its last-rendered value and re-checks the target before every write. With Preserve manual edits enabled (default):
Bindings are a pre-generation system, not a render-on-read layer. The rendered string is stored in the target field; consumers (themes, blocks, REST readers) get that stored value directly. Editing the source template doesn't propagate to existing posts until a trigger writes a fresh value to each one. When you edit a template that has bindings pointing at it, the plugin:
wp spintax bindings apply --binding=<id> --all from the CLI). The Stale badge only clears when the entire walk completes with zero failures — partial-failure walks keep the badge so you notice the divergence and retry.
200 bindings per site. The store is a single autoloaded option (~500 bytes per binding), and the cap keeps autoload memory bounded. If you genuinely need more, please open an issue with your use case.
The form rejects a handful of unsafe targets at save time:
_wp_, _edit_, _oembed_, etc.)._spintax_* slots used to store source, signatures, and cache versions.wp_posts columns like post_title, post_content, post_excerpt. These are not post-meta and writing to them via the meta API would silently create shadow rows.No — bindings are per-site. Each subsite manages its own. Use wp --url=site2 spintax bindings import --file=site1-bindings.json to copy bindings between subsites via the WP-CLI export/import round-trip.
Not in 2.0; bindings are admin-only. The wp spintax bindings WP-CLI surface covers staging→production sync scenarios. REST API exposure is tracked for a later release.
nested-spintax-for-acf. Is there a migration path?Yes. After activating Spintax 2.0, a dismissible admin banner points to Tools → Spintax Migration. The wizard scans for predecessor data, shows a per-row preview, and creates bindings deduped by (post type, target field). Per-post sources and variables are copied non-destructively — the old plugin's data stays in place until you delete it.
no_action_scheduler error path so users had to click-to-learn.Bulk Apply run_synchronously completed for binding <id> — wrote=N skipped=M cleared=K), so the "View details in Logs →" CTA on the success notice always lands on a populated page. Previously only failures logged.name (field_key)) is now stripped before the haystack filter, so "browse without retyping" works as documented.kind=acf_field exactly (previously hid only when kind=post_meta, leaving an empty-kind edge case where the row could render without hidden).test_run_now_handler_rejects_non_admin — replaces a conditional soft assertion with two unconditional invariants (walk-lock never acquired, no "Wrote N" success flash).{text, action_url, action_label} payload alongside legacy strings.?active_tab= query arg.<input list> + <datalist>). Group → field grouping, substring search across group / label / name, full ARIA combobox semantics, and clicking a row autofills both the field name and the stable ACF field key in one go.save_post is off and cron is disabled — live update on checkbox / select change so editors notice the problem before submitting.BulkApply::run_synchronously() — useful for dev sites without cron traffic and for installs without Action Scheduler. Walk-status badge ("Running (started Ns ago)") appears on the card while the per-binding walk lock is held.wp_ajax_spintax_dismiss_admin_notice endpoint. Whitelisted notice ids prevent the endpoint from filling wp_usermeta with arbitrary rows.{message, type} and the new rich payload. BindingsPage constructor now accepts an optional BulkApply for test injection. New shared Spintax\Support\TtlField helper backs the preset / custom TTL widget. Notice action-url is esc_url()-filtered so javascript: and other unsafe schemes are stripped.BindingApplier::plan() rejects bindings whose stored target.field_key no longer resolves to a field with the expected name (deleted, renamed, or re-assigned in ACF). Two new return codes: skip_acf_not_loaded (ACF deactivated since the binding was saved) and skip_invalid_acf_field (key + name disagreement). Closes a path where CLI-imported or imported-while-ACF-inactive bindings could write through update_field() to the wrong field.BindingApplier::read_target() and ::write_target() no longer fall back to plain update_post_meta() / get_post_meta() for kind = acf_field when ACF isn't loaded. The applier short-circuits at the runtime guard above, so the low-level methods are the sole writer for verified targets. Pre-2.0.3 the silent fallback could write the rendered value to a post-meta row ACF would never see again._spintax_binding_walk_failed_v_<id> flag. The final chunk gates stamp_last_applied_version() on the cumulative flag. 2.0.1 only checked the current chunk, so a multi-chunk walk that failed in chunk 1 and succeeded in the final chunk would still clear the Stale badge.WP_Error 'walk_in_progress'. Both enqueue() and run_synchronously() acquire a per-binding lock (option _spintax_binding_walk_lock_<id>) at walk start; stale locks older than one hour are auto-overwritten so a crashed walk doesn't permanently jam the binding.npm run lint:php and lint:php:fix moved to scripts/lint-php.sh / scripts/lint-php-fix.sh. The inline command tripped over bash-c quoting on Windows. .gitattributes enforces LF endings on shipped text files.wp spintax bindings import --overwrite help text updated to reflect the 2.0.1 (post_type, target.key) uniqueness contract.wp spintax bindings WP-CLI surface, variable scopes (global / per-binding / post context / ACF siblings), trigger options (save_post + per-binding cron), manual edit detection, template-edit propagation, reserved-key tiers.(post_type, field name) no longer coexist — they wrote to the same database row and silently raced. Tier 4 uniqueness now ignores target.kind. Existing pre-2.0.1 conflicts remain in the data store but the next save of either binding will reject.target.field_key and validate it against the live ACF field when ACF is loaded. Previously a missing or mistyped field key could route update_field() writes to a different field.skip_out_of_scope_type / skip_out_of_scope_status for posts that wouldn't match the binding's scope in live triggers. Two new applier return codes — total now 11 instead of 9.auto_seed_empty (default on; never clobbers existing content), regenerate_on_save, preserve_manual_edits (hash-tracks the last rendered value so external edits are detected), clear_on_empty. Cold-start behaviour documented to avoid false manual-edit positives.wp_schedule_event hooks per binding.%post_id%, %post_title%, %post_url%, %post_slug%, %post_date%, %post_modified%, %author_id%, %author_name% post-context variables — opt-in per binding.%acf_<field_name>% variables — opt-in per binding, exposes ACF sibling fields in the same group.wp spintax bindings list|apply|test|export|import — full WP-CLI surface for staging→production workflows and Action-Scheduler-less environments.nested-spintax-for-acf. Detects, previews, and imports legacy data deduped by (post_type, target.key). Original predecessor data is never deleted by the migration._spintax_* prefixes, wp_posts column names, and duplicate (post_type, target.kind, target.key) triples at form save.{plural <count>: form1|form2|form3} — pick the correct grammatical form by count. RU/UK/BE = 3 forms (one|few|many); EN/ES/PT/DE etc. = 2 forms (one|many). Count is a %var% reference or literal integer (resolved after variable expansion, so helper-var patterns via #set work). Locale comes from per-template post meta _spintax_locale or the WordPress site locale. Lenient at runtime: malformed constructs render verbatim with fullwidth braces instead of crashing the page. First spintax engine to treat plural as a first-class primitive.{}, []) always on; arity check (RU expects 3, EN expects 2) when locale is known.spintax-plurals.test.ts in casino-platform). Engine classes Plurals, PluralArityError, PluralFormError ship alongside Conditionals from 1.4.0.{?VAR?then|else} — render a branch based on whether a variable is set/non-empty (also {?!VAR?then} for inverted, optional else). Resolves both before and after %var% expansion, so conditionals inside variable values work too.соц., эл., Mr., Inc. no longer trigger sentence-end capitalisation of the next word. Covers Russian editorial/address/unit shorthands plus English titles and business suffixes.#set directive with an empty value (#set %x% =) no longer silently swallows the next directive on the following line.[<li>item</li>|<li>...]) are no longer mis-parsed as a <config> block.< sep > before |<and>, <или>)