PackageTrack
Sign in Get early access

wp-php-toolkit/html

HTML component for WordPress.

v0.9.0 297K downloads/mo #2989 most downloaded on Packagist wp-php-toolkit/html

What this package is like to depend on

Last release 3 months ago

18 May 2026

Release timing varies

gaps range from 2 weeks to 7 months

Rarely documented

notes for 9 of 40 stable releases

Nothing withdrawn

no release was ever pulled

1 years old

49 releases · first in 2025

18 releases in the last 12 months

see the full history below

Release timeline

19 releases · May 2025 to May 2026
2026
Release Pre-release

Releases

latest 49
  1. v0.9.0 18 May 2026
    Release notes

    Expose PHP fallback APIs from native wrappers

    Open source →
  2. v0.8.1 18 May 2026
    Release notes

    Expose PHP fallback APIs from native wrappers

    Open source →
  3. v0.8.0 no date
    Release notes

    Expose PHP fallback APIs from native wrappers

    Open source →
  4. v0.7.9 no date
    Release notes

    Expose PHP fallback APIs from native wrappers

    Open source →
  5. v0.7.8 18 May 2026
    Release notes

    Fix native wrapper autoload without extension

    Open source →
  6. v0.7.7 17 May 2026
    Release notes

    What it does

    Changes the public PHP classes to progressively use the native extension
    with no API-consumer burden.

    When the extension is installed and the native class advertises
    supports_public_api(), the public PHP class resolves to the native
    implementation. Otherwise it falls back to the moved PHP implementation.

    Implemented wiring:

    Component PHP integration in this PR
    HTML WP_HTML_Tag_Processor and WP_HTML_Processor load either
    empty native adapter subclasses or the moved PHP classes.
    XML XMLProcessor loads either NativeXMLProcessor directly or
    PHPXMLProcessor. The old cursor bridge is reduced to an empty native
    subclass.
    URL-in-text URLInTextProcessor loads either the native class
    through an empty adapter or PHPURLInTextProcessor.
    Data Liberation HTML Keeps the public subclass usable with the same
    native/PHP processor selection model instead of forcing the PHP path.

    The native wrapper files are intentionally tiny:

    components/DataLiberation/URL/class-nativeurlintextprocessorwrapper.php:
    5 lines

    • components/HTML/class-wp-html-native-processor-wrapper.php: 9 lines
    • components/HTML/class-wp-html-native-tag-processor-wrapper.php: 9
      lines
    • components/XML/class-xmlnativecursorprocessor.php: 5 lines

    Rationale

    The public class names should remain stable for consumers. Installing
    the native extension should be a progressive upgrade, not a new API they
    have to opt into.

    This PR keeps PHP responsible for loading and fallback only. Parser
    behavior belongs to the Rust classes already merged from #276 or to the
    moved PHP fallback classes.

    Implementation

    The moved PHP implementations live under component PHP/ paths. Public
    loader files choose the native class only when all of these are true:

    ! defined( 'WP_NATIVE_APIS_DISABLE_DEFAULTS' ) || ! WP_NATIVE_APIS_DISABLE_DEFAULTS
    class_exists( $native_class, false )
    method_exists( $native_class, 'supports_public_api' )
    $native_class::supports_public_api()

    There are no per-component WP_NATIVE_APIS_ENABLE_* switches. Tests and
    benchmarks can use the single global opt-out constant when they need to
    force fallback behavior.

    The benchmark harness now tolerates unsupported PHP fallback rows
    instead of aborting the run, while --require-native still fails if any
    native implementation row is unavailable.

    Benchmarks

    Benchmark run:
    https://github.com/WordPress/php-toolkit/actions/runs/25988013187

    Command:

    php -d extension=extensions/native-apis/target/release/libwp_native_apis.so \
      bin/benchmark-native-apis.php \
      --iterations=100 \
      --mode=both \
      --disable-native-defaults \
      --require-native

    Representative PHP-fallback vs native rows:

    Workload PHP wall (s) Native wall (s) Speedup
    html-tag-processor 0.240940 0.035885 6.71x
    html-processor 1.113778 0.135704 8.21x
    xml-processor 0.332820 0.063290 5.26x
    url-in-text-processor 3.853847 0.030515 126.29x

    The same run also completed all native fused/chunked benchmark rows for
    HTML, XML, and URL-in-text with --require-native enabled.

    Testing instructions

    Local checks run for the benchmark harness fixes:

    php -l bin/benchmark-native-apis.php

    Previously run locally before the #276 rebase:

    php -l components/HTML/class-wp-html-native-tag-processor-wrapper.php
    php -l components/HTML/class-wp-html-native-processor-wrapper.php
    php -l components/DataLiberation/URL/class-nativeurlintextprocessorwrapper.php
    php -l components/XML/class-xmlnativecursorprocessor.php
    php -l components/XML/class-xmlprocessor.php
    php -l components/HTML/class-wp-html-tag-processor.php
    php -l components/HTML/class-wp-html-processor.php
    php -l components/DataLiberation/URL/class-urlintextprocessor.php
    vendor/bin/phpunit components/HTML/Tests/NativeHTMLConformanceTest.php
    vendor/bin/phpunit components/XML/Tests/NativeXMLConformanceTest.php
    vendor/bin/phpunit components/DataLiberation/Tests/URLInTextProcessorTest.php components/DataLiberation/Tests/URLInTextProcessorWHATWGComplianceTest.php components/DataLiberation/Tests/WPURLTest.php
    vendor/bin/phpcs --standard=phpcs.xml components/DataLiberation/URL/class-nativeurlintextprocessorwrapper.php components/DataLiberation/URL/class-urlintextprocessor.php components/HTML/class-wp-html-native-processor-wrapper.php components/HTML/class-wp-html-native-tag-processor-wrapper.php components/HTML/class-wp-html-processor.php components/HTML/class-wp-html-tag-processor.php components/XML/class-xmlnativecursorprocessor.php components/XML/class-xmlprocessor.php components/HTML/Tests/NativeHTMLConformanceTest.php components/XML/Tests/NativeXMLConformanceTest.php
    git diff --check

    GitHub Actions is the authoritative extension build/load check for this
    PR because this local machine does not have php-config.

    Open source →
  7. v0.7.6 16 May 2026
    Release notes

    What it does

    Moves the existing public API implementation files to *php* sidecar
    filenames with no line changes.

    This is intentionally only a file-move PR. It does not add public
    loaders, native wrapper classes, or behavior changes; those stay in the
    stacked PR #271.

    Rationale

    This gives reviewers a clean baseline where GitHub shows the large
    implementation files as renames before #271 adds the native integration
    and loader changes.

    Implementation

    Moved these files at 100% similarity:

    • components/HTML/class-wp-html-tag-processor.php ->
      components/HTML/class-wp-html-php-tag-processor.php
    • components/HTML/class-wp-html-processor.php ->
      components/HTML/class-wp-html-php-processor.php
    • components/XML/class-xmlprocessor.php ->
      components/XML/class-phpxmlprocessor.php
    • components/DataLiberation/URL/class-urlintextprocessor.php ->
      components/DataLiberation/URL/class-phpurlintextprocessor.php

    The class names are deliberately unchanged in this PR so the diff
    remains move-only.

    Testing instructions

    composer dump-autoload
    php -l components/HTML/class-wp-html-php-tag-processor.php components/HTML/class-wp-html-php-processor.php components/XML/class-phpxmlprocessor.php components/DataLiberation/URL/class-phpurlintextprocessor.php
    vendor/bin/phpunit components/XML/Tests/
    vendor/bin/phpunit components/DataLiberation/Tests/URLInTextProcessorTest.php
    vendor/bin/phpunit components/DataLiberation/Tests/MarkupProcessorConsumerTest.php components/DataLiberation/Tests/HTMLEntityReaderTest.php
    vendor/bin/phpunit components/Markdown/Tests/MarkdownConsumerTest.php
    git diff origin/trunk...HEAD --check
    Open source →
  8. v0.7.5 03 May 2026
    Release notes

    Summary

    The toolkit now documents itself using only its own runtime and its
    own structured-data parsers. No Python in CI; no hand-rolled regex over
    markdown or HTML.

    What changed

    Was Now
    bin/_docs_components.py (200 lines, dead-code dicts) deleted
    bin/_load_catalog.py (370 lines: hand-rolled YAML-subset parser,
    regex section splitter, regex snippet extractor) replaced with proper
    parsers
    bin/build-reference.py (220 lines) bin/build-reference.php
    bin/run-snippets.py (230 lines) bin/run-snippets.php
    bin/serve-docs.py (50 lines) bin/serve-docs.php (uses php -S
    with router)

    Parsers

    Surface Parser
    README YAML frontmatter (slug, title, install, credit_*,
    see_also) Webuni\FrontMatter\FrontMatter::parse() — already
    vendored under components/Markdown/vendor-patched/ for the Markdown
    component. Single-line, multi-line `key: `, and YAML lists all behave
    correctly. The same parser GitHub uses for the README's frontmatter
    table.
    Markdown body → AST League\CommonMark\Parser\MarkdownParser +
    walking the document. Section boundaries = Heading nodes at level 2.
    Snippets = HtmlBlock (<!-- snippet: -->) → FencedCode (info=php)
    tuples. Expected-output = HtmlBlock (<!-- expected-output -->) →
    FencedCode pair. Body content rendered via
    HtmlRenderer::renderNodes() so raw HTML round-trips verbatim.
    Pitfall callouts (HtmlBlocks of the form <p>Footgun: …</p>)
    WP_HTML_Tag_Processor — walks tokens, confirms a <p> opener,
    finds the first inner #text node, classifies, strips the Footgun: /
    Gotcha: prefix via set_modifiable_text(), then slices off the outer
    <p>...</p> by length (no regex).
    Lede paragraph → inline HTML (no outer <p>) Render the lede
    Paragraph node's inline children directly via
    HtmlRenderer::renderNodes() instead of slicing afterward.
    Snippet metadata comment (`<!-- snippet: filename: x.php\nrunnable:
    true\n-->`) String slicing of literal <!-- / --> delimiters — no
    regex.
    --update writing captured stdout back into a README **CommonMark
    AST** locates the snippet's exact line range; line-by-line splice. No
    regex over the README.

    Behavioural parity

    • 87/87 snippets match their captured stdout. The PHP normalizer
      mirrors the Python regex set 1:1, so existing expected-output blocks
      stay valid.
    • docs/reference/*.html render correctly: 9 <php-snippet> + 9
      fallback + 9 expected-output triples on html.html, 4 pitfall callouts
      (the bold-lead pattern is preserved), see-also list intact.
    • --update verified end-to-end:
    • When an expected-output block has drifted, the rewritten content is
      byte-for-byte the original.
    • When no expected-output exists for a snippet, --update inserts a
      fresh block in the right place; resulting README is byte-identical to
      one with the block authored manually.

    Frontmatter format change: see_also is a proper YAML list

    # Before (repeated keys — not standard YAML)
    see_also: a | A | reason
    see_also: b | B | reason
    
    # After
    see_also:
      - a | A | reason
      - b | B | reason

    Webuni\FrontMatter\FrontMatter correctly types it as a sequence; any
    frontmatter-aware tool reading the README sees the same shape. All 18
    component READMEs migrated.

    Workflows simplified

    • snippet-tests.yml drops the actions/setup-python step.
    • docs.yml swaps python3 bin/build-reference.py for php bin/build-reference.php.

    Remaining preg_* calls (all on plain text, not HTML)

    • slugify() — heading text → URL-safe slug.
    • normalize() — scrubs noise from snippet stdout (tempfile paths, git
      hashes, timestamps).
    • One pattern in run-snippets that matches the require '...autoload.php'; line in the snippet's PHP source to inject the
      local-prelude polyfill.

    These operate on plain strings, not HTML, so they're not what the "no
    regex over HTML" rule was about.

    Test plan

    • Verify docs snippets workflow passes (87/87).
    • Deploy docs to GitHub Pages runs cleanly on push to trunk.
    • Local preview: bash bin/build-docs-bundle.sh && php bin/serve-docs.phphttp://localhost:8787 renders all reference pages
      with snippets.
    • php bin/run-snippets.php --update (no-op, no drift) leaves all
      READMEs untouched.
    Open source →
  9. v0.7.4 no date
    Release notes

    Summary

    The toolkit now documents itself using only its own runtime and its
    own structured-data parsers. No Python in CI; no hand-rolled regex over
    markdown or HTML.

    What changed

    Was Now
    bin/_docs_components.py (200 lines, dead-code dicts) deleted
    bin/_load_catalog.py (370 lines: hand-rolled YAML-subset parser,
    regex section splitter, regex snippet extractor) replaced with proper
    parsers
    bin/build-reference.py (220 lines) bin/build-reference.php
    bin/run-snippets.py (230 lines) bin/run-snippets.php
    bin/serve-docs.py (50 lines) bin/serve-docs.php (uses php -S
    with router)

    Parsers

    Surface Parser
    README YAML frontmatter (slug, title, install, credit_*,
    see_also) Webuni\FrontMatter\FrontMatter::parse() — already
    vendored under components/Markdown/vendor-patched/ for the Markdown
    component. Single-line, multi-line `key: `, and YAML lists all behave
    correctly. The same parser GitHub uses for the README's frontmatter
    table.
    Markdown body → AST League\CommonMark\Parser\MarkdownParser +
    walking the document. Section boundaries = Heading nodes at level 2.
    Snippets = HtmlBlock (<!-- snippet: -->) → FencedCode (info=php)
    tuples. Expected-output = HtmlBlock (<!-- expected-output -->) →
    FencedCode pair. Body content rendered via
    HtmlRenderer::renderNodes() so raw HTML round-trips verbatim.
    Pitfall callouts (HtmlBlocks of the form <p>Footgun: …</p>)
    WP_HTML_Tag_Processor — walks tokens, confirms a <p> opener,
    finds the first inner #text node, classifies, strips the Footgun: /
    Gotcha: prefix via set_modifiable_text(), then slices off the outer
    <p>...</p> by length (no regex).
    Lede paragraph → inline HTML (no outer <p>) Render the lede
    Paragraph node's inline children directly via
    HtmlRenderer::renderNodes() instead of slicing afterward.
    Snippet metadata comment (`<!-- snippet: filename: x.php\nrunnable:
    true\n-->`) String slicing of literal <!-- / --> delimiters — no
    regex.
    --update writing captured stdout back into a README **CommonMark
    AST** locates the snippet's exact line range; line-by-line splice. No
    regex over the README.

    Behavioural parity

    • 87/87 snippets match their captured stdout. The PHP normalizer
      mirrors the Python regex set 1:1, so existing expected-output blocks
      stay valid.
    • docs/reference/*.html render correctly: 9 <php-snippet> + 9
      fallback + 9 expected-output triples on html.html, 4 pitfall callouts
      (the bold-lead pattern is preserved), see-also list intact.
    • --update verified end-to-end:
    • When an expected-output block has drifted, the rewritten content is
      byte-for-byte the original.
    • When no expected-output exists for a snippet, --update inserts a
      fresh block in the right place; resulting README is byte-identical to
      one with the block authored manually.

    Frontmatter format change: see_also is a proper YAML list

    # Before (repeated keys — not standard YAML)
    see_also: a | A | reason
    see_also: b | B | reason
    
    # After
    see_also:
      - a | A | reason
      - b | B | reason

    Webuni\FrontMatter\FrontMatter correctly types it as a sequence; any
    frontmatter-aware tool reading the README sees the same shape. All 18
    component READMEs migrated.

    Workflows simplified

    • snippet-tests.yml drops the actions/setup-python step.
    • docs.yml swaps python3 bin/build-reference.py for php bin/build-reference.php.

    Remaining preg_* calls (all on plain text, not HTML)

    • slugify() — heading text → URL-safe slug.
    • normalize() — scrubs noise from snippet stdout (tempfile paths, git
      hashes, timestamps).
    • One pattern in run-snippets that matches the require '...autoload.php'; line in the snippet's PHP source to inject the
      local-prelude polyfill.

    These operate on plain strings, not HTML, so they're not what the "no
    regex over HTML" rule was about.

    Test plan

    • Verify docs snippets workflow passes (87/87).
    • Deploy docs to GitHub Pages runs cleanly on push to trunk.
    • Local preview: bash bin/build-docs-bundle.sh && php bin/serve-docs.phphttp://localhost:8787 renders all reference pages
      with snippets.
    • php bin/run-snippets.php --update (no-op, no drift) leaves all
      READMEs untouched.
    Open source →
  10. v0.7.3 no date

    Nothing published for this version

  11. v0.7.2 30 Apr 2026

    Nothing published for this version

  12. v0.7.1 30 Apr 2026

    Nothing published for this version

  13. v0.7.0 29 Apr 2026

    Nothing published for this version

  14. v0.6.2 10 Apr 2026

    Nothing published for this version

  15. v0.6.1 no date

    Nothing published for this version

  16. v0.6.0 no date

    Nothing published for this version

  17. v0.5.1 08 Sep 2025

    Nothing published for this version

  18. v0.5.0 no date

    Nothing published for this version

  19. v0.4.1 no date

    Nothing published for this version

  20. v0.4.0 no date

    Nothing published for this version

  21. v0.3.1 no date

    Nothing published for this version

  22. v0.3.0 no date

    Nothing published for this version

  23. v0.2.0 no date

    Nothing published for this version

  24. v0.1.5 no date

    Nothing published for this version

  25. v0.1.4 no date

    Nothing published for this version

  26. v0.1.3 no date

    Nothing published for this version

  27. v0.1.2 08 Sep 2025

    Nothing published for this version

  28. v0.1.1 no date

    Nothing published for this version

  29. v0.1.0 no date

    Nothing published for this version

  30. 0.0.19 no date

    Nothing published for this version

  31. 0.0.18 no date

    Nothing published for this version

  32. 0.0.17 no date

    Nothing published for this version

  33. 0.0.16 no date

    Nothing published for this version

  34. 0.0.15 no date

    Nothing published for this version

  35. v0.0.15-alpha no date pre-release

    Nothing published for this version

  36. 0.0.14 no date

    Nothing published for this version

  37. 0.0.13 no date

    Nothing published for this version

  38. 0.0.12 no date

    Nothing published for this version

  39. 0.0.11 06 Sep 2025

    Nothing published for this version

  40. v0.0.8-alpha 08 Sep 2025 pre-release

    Nothing published for this version

  41. 0.0.7 06 Sep 2025

    Nothing published for this version

  42. v0.0.7-alpha 08 Sep 2025 pre-release

    Nothing published for this version

  43. 0.0.6 06 Sep 2025

    Nothing published for this version

  44. v0.0.6-alpha 08 Sep 2025 pre-release

    Nothing published for this version

  45. v0.0.5-alpha 20 May 2025 pre-release

    Nothing published for this version

  46. v0.0.4-alpha no date pre-release

    Nothing published for this version

  47. v0.0.3-alpha no date pre-release

    Nothing published for this version

  48. v0.0.2-alpha no date pre-release

    Nothing published for this version

  49. v0.0.1-alpha no date pre-release

    Nothing published for this version

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive