Selectors

Atelier SVG provides CSS-like selectors for querying elements and a visitor pattern for traversing and transforming the element tree.

SelectorMatcher

Matches elements against CSS-like selector strings.

Supported selectors:

Syntax Description
* Universal: matches any
rect Tag name
#myId ID selector
.myClass Class selector
[attr] Attribute exists
[attr="value"] Exact match
[attr^="value"] Starts with
[attr$="value"] Ends with
[attr*="value"] Contains
[attr~="value"] Word in space-separated list
[attr|="value"] Exact or dash-prefixed
use Atelier\Svg\Selector\SelectorMatcher;

$matcher = new SelectorMatcher();
$matcher->matches($element, 'rect');            // true if <rect>
$matcher->matches($element, '#header');         // true if id="header"
$matcher->matches($element, '.active');         // true if has class "active"
$matcher->matches($element, '[fill="red"]');    // true if fill="red"

Traverser

Performs a depth-first traversal of the element tree, applying a visitor to every element.

use Atelier\Svg\Visitor\Traverser;

$traverser = new Traverser($visitor);
$traverser->traverse($rootElement);

The traverser visits the element, then recursively visits each child of container elements.

VisitorInterface

The base contract. Implement visit(ElementInterface $element): mixed.

AbstractVisitor

Template class with beforeVisit, doVisit, and afterVisit hooks. Subclass and implement doVisit for your logic.

use Atelier\Svg\Visitor\AbstractVisitor;
use Atelier\Svg\Element\ElementInterface;

class CountVisitor extends AbstractVisitor
{
    public int $count = 0;

    protected function doVisit(ElementInterface $element): mixed
    {
        $this->count++;
        return null;
    }
}

CallbackVisitor

Wraps a closure so you can traverse without creating a dedicated class.

use Atelier\Svg\Visitor\CallbackVisitor;
use Atelier\Svg\Visitor\Traverser;

$visitor = new CallbackVisitor(function ($element) {
    $element->addClass('visited');
    return true; // return false to stop
});

$traverser = new Traverser($visitor);
$traverser->traverse($root);

QueryVisitor

Collects elements matching a selector during traversal.

use Atelier\Svg\Visitor\QueryVisitor;
use Atelier\Svg\Selector\SelectorMatcher;
use Atelier\Svg\Visitor\Traverser;

$query = new QueryVisitor('.highlight', new SelectorMatcher());
$traverser = new Traverser($query);
$traverser->traverse($root);

$query->getMatches();      // ElementInterface[]
$query->getFirstMatch();   // ElementInterface|null
$query->hasMatches();      // bool
$query->getMatchCount();   // int

Pass findFirst: true to the constructor to stop after the first match.

TypedVisitor

Dispatches to type-specific visitXxx methods based on the element class name. The Element suffix is stripped automatically.

use Atelier\Svg\Visitor\TypedVisitor;
use Atelier\Svg\Element\Shape\CircleElement;
use Atelier\Svg\Element\Shape\RectElement;
use Atelier\Svg\Element\ElementInterface;

class ColorVisitor extends TypedVisitor
{
    protected function visitCircle(CircleElement $circle): mixed
    {
        $circle->setFill('#3b82f6');
        return null;
    }

    protected function visitRect(RectElement $rect): mixed
    {
        $rect->setFill('#10b981');
        return null;
    }

    protected function visitDefault(ElementInterface $element): mixed
    {
        return null; // required fallback
    }
}

Method names are cached per class for performance.

TransformVisitor

Applies a 2D affine transformation matrix to elements during traversal.

use Atelier\Svg\Visitor\TransformVisitor;

$visitor = new TransformVisitor();
$visitor->setTransformMatrix([
    'a' => 1.0, 'b' => 0.0,
    'c' => 0.0, 'd' => 1.0,
    'e' => 50.0, 'f' => 25.0,  // translate(50, 25)
]);

// Parse existing transforms, merge matrices
$matrix = $visitor->parseTransformToMatrix('matrix(1 0 0 1 10 10)');
$merged = $visitor->mergeMatrices($matrix, $otherMatrix);

See also