phly/microformats2 (0.4.0)

Published 2026-09-20 19:25:58 +00:00 by mwop in phly/microformats2

Installation

{
	"repositories": [{
			"type": "composer",
			"url": ""
		}
	]
}
composer require phly/microformats2:0.4.0

About this package

Microformats2 Parser and object representation

phly/microformats2

A PHP 8.5+ Microformats2 parser with Webmention classification support.

Why PHP 8.5?

  • PHP 8.4 introduced Dom\HtmlDocument, which includes querySelector() and querySelectorAll(), simplifying DOM traversal and detection of elements with specific attributes or CSS classes.
  • PHP 8.5 introduced the Uri namespace and its URI parsing/representation implementations. This library uses Uri\WhatWg\Url for validating, parsing, and manipulating URLs discovered in microformats.

Specifications

Specifications are pulled from the Microformats wiki and converted to markdown in ./spec/.

Installation

composer require phly/microformats2

Usage

Parsing Microformats

use Phly\Microformats2\Parser;
use Phly\Microformats2\MicroformatType;
use Phly\Microformats2\MicroformatPropertyType;

$parser = new Parser();

// Parse HTML with a base URL for resolving relative URLs
$result = $parser->parse($html, $sourceUrl);

// Access parsed microformat items
foreach ($result->items as $item) {
    // $item is a Microformat object
    echo $item->type[0]->value;  // 'h-entry' (MicroformatType enum)
    $item->type[0] === MicroformatType::HEntry; // true
    echo $item->properties;      // ['name' => ['My Post'], ...]
    echo $item->id;              // DOM element id, or null
    echo $item->children;        // Nested Microformat objects
    echo $item->findParent();    // Parent Microformat, or null
}

// Access rel microformats
$authorUrls = $result->rels['author'] ?? [];
$meUrls     = $result->rels['me'] ?? [];

// Serialize to JSON (mf2 JSON format)
$json = json_encode($result);

Working with Microformat Properties

Microformat properties are stored as arrays keyed by property name. The structure depends on the property type:

$entry = $result->items[0];

// p-* properties (text): array of strings
$name = $entry->properties['name'][0];      // 'My Post'
$note = $entry->properties['note'][0];      // 'A short note'

// u-* properties (URLs): array of strings (resolved URLs)
$url   = $entry->properties['url'][0];      // 'https://example.com/post'
$photo = $entry->properties['photo'][0];    // 'https://example.com/photo.jpg'

// dt-* properties (dates/times): array of strings
$published = $entry->properties['published'][0]; // '2024-01-15T10:30:00Z'

// Nested microformats (e.g., p-author with an h-card)
$author = $entry->properties['author'][0];
if ($author instanceof \Phly\Microformats2\Microformat) {
    echo $author->type[0]->value;             // 'h-card'
    echo $author->properties['name'][0];      // 'Jane Doe'
    echo $author->properties['photo'][0];     // 'https://example.com/photo.jpg'
}

Property Types

The library provides a MicroformatPropertyType enum for type-safe property references. This enum covers all officially documented properties from the microformats2 wiki.

use Phly\Microformats2\MicroformatPropertyType;

// Look up a property type by its class name
$type = MicroformatPropertyType::tryFrom('p-name');
// Returns MicroformatPropertyType::PName

$type = MicroformatPropertyType::tryFrom('u-in-reply-to');
// Returns MicroformatPropertyType::UInReplyTo

$type = MicroformatPropertyType::tryFrom('myorg-p-name');
// Returns null (unknown/vendor-prefixed properties are not in the enum)

// Get the type prefix
MicroformatPropertyType::PName->prefix();     // 'p'
MicroformatPropertyType::UUrl->prefix();      // 'u'
MicroformatPropertyType::DTPublished->prefix(); // 'dt'
MicroformatPropertyType::EContent->prefix();  // 'e'

// Get the property name without the prefix
MicroformatPropertyType::PName->name();       // 'name'
MicroformatPropertyType::UInReplyTo->name();  // 'in-reply-to'

Querying Property Types

The Microformat object exposes methods to check property types:

$entry = $result->items[0];

// Get the property type for a specific property
$type = $entry->getPropertyType('name');
// Returns MicroformatPropertyType::PName

$type = $entry->getPropertyType('url');
// Returns MicroformatPropertyType::UUrl

// Check if properties of specific types exist
$hasText = $entry->hasPropertiesOfType([MicroformatPropertyType::PName]);
$hasUrl  = $entry->hasPropertiesOfType([MicroformatPropertyType::UUrl]);

// Check by prefix
$hasPProperties = $entry->hasPropertiesWithPrefix(['p']); // true if any p-* exist
$hasEProperties = $entry->hasPropertiesWithPrefix(['e']); // true if any e-* exist

// Get the raw class name for a property
$rawClass = $entry->getRawPropertyClass('name'); // 'p-name'

Vendor-Prefixed Properties

Properties with vendor prefixes (e.g., myorg-p-name) are recognized by the parser but represented as MicroformatPropertyType::Extension:

// A vendor-prefixed property is parsed but uses the Extension type
$type = $entry->getPropertyType('name'); // MicroformatPropertyType::Extension
$raw  = $entry->getRawPropertyClass('name'); // 'myorg-p-name'

Accessor Methods

The Microformat class provides convenient accessor methods for common property lookups:

$entry = $result->items[0];

// getProperty() — returns the first string value, or null
$name = $entry->getProperty('name');         // 'My Post'
$note = $entry->getProperty('note');         // 'A short note' or null
$miss = $entry->getProperty('nonexistent');  // null

// getProperties() — returns all string values as an array
$categories = $entry->getProperties('category'); // ['php', 'web', 'html']

// Content accessors (for e-content properties)
// getContent() and related methods access the 'content' property specifically.
// Note: getProperty('content') returns null — use these instead.
$htmlContent = $entry->getHtmlContent();     // Raw HTML from first e-content
$textContent = $entry->getTextContent();     // Plain text from first e-content
$allHtml     = $entry->getAllHtmlContent();   // Array of all raw HTML values
$allText     = $entry->getAllTextContent();   // Array of all plain text values

Accessing the DOM Element

Each Microformat provides access to its underlying DOM element:

$mf = $result->items[0];

// Get the DOM element
$element = $mf->getElement();

// Get the owning HTML document
$doc = $mf->getDocument();

// The element is a Dom\Element — use standard DOM methods
$element->getAttribute('class');
$element->textContent;
$element->innerHTML;

Webmention Classification

The library can classify webmentions by analyzing the source page's microformats relative to the target URL. This implements the response type detection from the IndieWeb.

Classifying a Webmention

use Phly\Microformats2\Parser;
use Phly\Microformats2\Webmention\Classifier;

$parser = new Parser();

// Parse the source page (the page that sent the webmention)
$result = $parser->parse($sourceHtml, $sourceUrl);

// Classify the webmention
$classifier = new Classifier(
    result:     $result,
    sourceUrl:  $sourceUrl,   // URL of the page sending the mention
    targetUrl:  $targetUrl,   // URL being mentioned
    urlFetcher: fn(string $url): ?\Phly\Microformats2\Result => $this->fetchAndParse($url),
);

$webmention = $classifier->classify();

The $urlFetcher is a callable that fetches a URL and returns a parsed Result, or null on failure. It is used by the AuthorResolver to fetch external author pages (e.g., when the author is specified as a URL rather than an embedded h-card). If omitted, external author resolution is skipped.

Webmention Result Types

The classify() method returns one of five result types. All extend WebmentionResult which provides $source and $target properties.

Deletion

The source page no longer links to the target. The content has been deleted or removed.

use Phly\Microformats2\Webmention\Deletion;

/** @var Deletion $webmention */
$webmention->source; // 'https://source.example/post'
$webmention->target; // 'https://target.example/post'

Comment

The source page is a reply to the target. Detected via u-in-reply-to.

use Phly\Microformats2\Webmention\Comment;

/** @var Comment $webmention */
$webmention->source;  // 'https://source.example/reply'
$webmention->target;  // 'https://target.example/post'
$webmention->comment; // Webmention (h-entry) — the reply itself
$webmention->author;  // Webmention (h-card) or null — the reply's author

// Access the comment content using accessor methods
$entry = $webmention->comment;
$name  = $entry->getProperty('name');     // Reply title
$html  = $entry->getHtmlContent();        // Reply HTML
$text  = $entry->getTextContent();        // Reply plain text

// Access the author
if ($webmention->author !== null) {
    $authorName  = $webmention->author->getProperty('name');
    $authorPhoto = $webmention->author->getProperty('photo');
    $authorUrl   = $webmention->author->getProperty('url');
}

Repost

The source page is a repost/boost of the target. Detected via u-repost-of.

use Phly\Microformats2\Webmention\Repost;

/** @var Repost $webmention */
$webmention->source; // 'https://source.example/repost'
$webmention->target; // 'https://target.example/post'
$webmention->author; // Webmention (h-card) or null — who reposted

if ($webmention->author !== null) {
    $name  = $webmention->author->getProperty('name');
    $photo = $webmention->author->getProperty('photo');
}

Like

The source page is a like/favorite of the target. Detected via u-like-of.

use Phly\Microformats2\Webmention\Like;

/** @var Like $webmention */
$webmention->source; // 'https://source.example/like'
$webmention->target; // 'https://target.example/post'
$webmention->author; // Webmention (h-card) or null — who liked

if ($webmention->author !== null) {
    $name  = $webmention->author->getProperty('name');
    $photo = $webmention->author->getProperty('photo');
}

Mention

A generic mention — the source page links to the target but does not have a specific response type.

use Phly\Microformats2\Webmention\Mention;

/** @var Ment

Dependencies

Dependencies

ID Version
php ~8.5.0

Development dependencies

ID Version
laminas/laminas-coding-standard ^3.1
phpunit/phpunit ^13.3
Details
Composer
2026-09-20 19:25:58 +00:00
15
Matthew Weier O'Phinney
BSD-2-Clause
42 KiB
Assets (1)
Versions (1) View all
0.4.0 2026-09-20