Search by

zegnat / linkextractor

Zegnat

A package that tries to figure out what resources an HTML document links to.

Package info

github.com/Zegnat/php-linkextractor

Language:HTML

pkg:composer/zegnat/linkextractor

Statistics

Installs: 7

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

0.3.0 2026-07-12 23:08 UTC

README

The LinkExtractor tries to figure out what resources an HTML document links to.

This tries to solve 2 questions:

  1. what constitutes a link between an HTML document and another resource, and
  2. how to supply them in a useful (resolved) format.

The class is able to get (and resolve) every URL from a pre-parsed DOM in accordance with where the HTML5 specification allows URLs to be defined. The HTML5 specification is followed after a GitHub discussion about what a link is, and further in person discussion at IWC Berlin 2017.

Using versions < 1.0

The current function prototypes are the same as those that will be in 1.0.0. This means you can write the method calls in your code right now and update to 1.0.0 without breaking any of your calls later.

function extract(): array
function linksTo(string $url): bool

If you rely on specific output or exceptions from these methods however, there might be breaking changes before version 1.0.0. Take a look at the roadmap to version 1.0.0 to get an idea of what is likely to still be changed.

Don’t wait for 1.0.0 with using this library if you need it! Instead, start using it and leave the ever-important feedback.

Install

Via Composer

$ composer require zegnat/linkextractor

Usage

// Parse the HTML into a DOMDocument:
$dom = new \DOMDocument();
$dom->loadHTML(file_get_contents('http://example.com/index.html'));
// Initiate the extractor with the document and its URL:
$extractor = new \Zegnat\LinkExtractor\LinkExtractor($dom, 'http://example.com/index.html');
var_dump(
    $extractor->linksTo('https://github.com/'),
    $extractor->linksTo('http://www.iana.org/domains/example')
);
/*
bool(false)
bool(true)
*/

On PHP 8.4 and newer you can instead pass a document parsed with the new \Dom\HTMLDocument API and its standards-compliant HTML parser:

$dom = \Dom\HTMLDocument::createFromString(
    file_get_contents('http://example.com/index.html')
);
$extractor = new \Zegnat\LinkExtractor\LinkExtractor($dom, 'http://example.com/index.html');

Supplying your own XPath

An optional third constructor argument takes an already created \DOMXPath or \Dom\XPath instance for the same document. If none is given, one will be created.

Parsing a srcset attribute on its own

A srcset attribute does not hold URLs, it holds a comma-separated list of image candidate strings. The parser the LinkExtractor uses for them is a public class of its own, so it can be used without the rest of this library:

use Zegnat\LinkExtractor\SrcsetParser;

$candidates = SrcsetParser::parse('small.png, large.png 2x, broken.png 1x 2x');
/*
[
    ['url' => 'small.png', 'width' => null, 'density' => null],
    ['url' => 'large.png', 'width' => null, 'density' => 2.0],
]
*/

URLs are returned exactly as they were written, as the parser does not know what document they came from; resolve them yourself. Image candidates the HTML Standard rejects are left out, which is why broken.png is missing above: a candidate cannot carry two pixel density descriptors.

Supported PHP versions

This library requires PHP 7.0 or newer and is tested against PHP 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 and 8.5.

From PHP 8.4 onwards it accepts both \DOMNode and \Dom\Node.

Testing

Tests run on every supported PHP version in containers, orchestrated by Castor. Each PHP version and its dependencies live entirely in their container, so only Castor and a container runtime are needed on the host:

$ castor matrix     # run the whole matrix
$ castor test 8.4   # run a single version
$ castor coverage   # run with code coverage and enforce the minimum
$ castor analyse    # run PHPStan over the library and castor.php
$ castor style      # check the code style against PER Coding Style

Both Podman and Docker work. Whichever of the two is on your PATH is used, preferring Podman when both are installed because it needs no root. Pass --runtime to any of the tasks to choose explicitly:

$ castor test 8.4 --runtime docker

Some test fixtures under tests/files are copied verbatim from external open-source projects under their respective licenses; see CREDITS.md for their sources and the LICENSES directory (REUSE).

License

The BSD Zero Clause License (0BSD). Please see the LICENSE file for more information.

Files under vendored/ are copied from other projects and keep their own licenses. Each one has a .license sidecar naming its copyright and license, with the full text in LICENSES/ (REUSE).