Transformer
Mit Transformern kannst du die hervorgehobene Ausgabe in jeder Phase neu gestalten – vom rohen Quelltext über das Token-Raster bis zum fertigen HAST-Baum und HTML. shikiphp übernimmt Shikis Transformermodell Hook für Hook, sodass sich Transformer genauso verhalten wie ihre JavaScript-Entsprechungen.
Übergib sie über die Option transformers:
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\Notation\NotationHighlight;
use Shikiphp\Transformer\Notation\NotationDiff;
$code = <<<'JS'
console.log('this line is highlighted') // [!code highlight]
console.log('old line') // [!code --]
console.log('new line') // [!code ++]
JS;
echo Shikiphp::codeToHtml($code, [
'lang' => 'js',
'theme' => 'vitesse-dark',
'transformers' => [
new NotationHighlight(),
new NotationDiff(),
],
]);
Die Pipeline
Jeder Transformer ist ein Objekt, das Shikiphp\Transformer\Transformer implementiert.
Die Pipeline führt die Hooks der Reihe nach beim Aufbau eines einzelnen Blocks aus:
| Hook | Signatur | Zeitpunkt |
|---|---|---|
preprocess |
(string $code, array &$options, $context): ?string |
vor der Tokenisierung; kann den Quelltext umschreiben und Optionen ändern |
tokens |
(array $tokens, $context): ?array |
nach der Tokenisierung, auf dem 2D-ThemedToken-Raster |
root |
(Element $el, $context): ?Element |
auf der HAST-Wurzel |
pre |
(Element $el, $context): ?Element |
auf dem <pre>-Element |
code |
(Element $el, $context): ?Element |
auf dem <code>-Element |
line |
(Element $el, int $line, $context): ?Element |
pro Zeilenelement (ab 1 gezählt) |
span |
(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element |
pro Token-Span |
postprocess |
(string $html, array $options, $context): ?string |
auf dem zuletzt serialisierten HTML-String |
Jeder Hook ist optional. Ein Rückgabewert ersetzt die Eingabe; null
lässt sie unverändert.
enforce() legt die Reihenfolge der Transformer fest: 'pre' wird zuerst ausgeführt, 'post' zuletzt und null (der Standard) dazwischen, in der Reihenfolge ihrer Registrierung.
Transformer schreiben
Erweitere Shikiphp\Transformer\AbstractTransformer, damit du nur die Hooks überschreiben musst,
die du benötigst; die übrigen bleiben wirkungslos. Der Transformer-Kontext stellt
addClassToHast() bereit, um Elementen Klassen hinzuzufügen.
use Shikiphp\Hast\Element;
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\AbstractTransformer;
use Shikiphp\Transformer\TransformerContext;
final class AddLanguageClass extends AbstractTransformer
{
public function pre(Element $element, TransformerContext $context): ?Element
{
if ($context->lang !== null) {
$context->addClassToHast($element, 'language-' . $context->lang);
}
return $element;
}
}
echo Shikiphp::codeToHtml('const answer = 42', [
'lang' => 'js',
'theme' => 'nord',
'transformers' => [new AddLanguageClass()],
]);
// <pre class="shiki nord language-js" …>
Integrierte Transformer
Notations-Transformer
Diese lesen spezielle // [!code …]-Kommentare im Quelltext (die Kommentar-Syntax der Sprache wird automatisch erkannt) und wandeln sie in Klassen um; der Kommentar selbst wird entfernt. Sie sind direkte Portierungen des @shikijs/transformers-Notationssatzes.
NotationHighlight
Shikiphp\Transformer\Notation\NotationHighlight — // [!code highlight] (oder
// [!code hl]) fügt der Zeile die Klasse highlighted hinzu.
console.log('kept plain')
console.log('this line is highlighted') // [!code highlight]
NotationDiff
Shikiphp\Transformer\Notation\NotationDiff — // [!code ++] markiert eine Zeile als
hinzugefügt (diff add), // [!code --] als entfernt (diff remove).
console.log('old line') // [!code --]
console.log('new line') // [!code ++]
NotationFocus
Shikiphp\Transformer\Notation\NotationFocus — // [!code focus] fügt die
Klasse focused hinzu.
console.log('focus on me') // [!code focus]
NotationErrorLevel
Shikiphp\Transformer\Notation\NotationErrorLevel — // [!code error],
// [!code warning] und // [!code info] fügen die Klasse highlighted sowie
error / warning / info hinzu.
throwSomething() // [!code error]
maybeRisky() // [!code warning]
NotationWordHighlight
Shikiphp\Transformer\Notation\NotationWordHighlight — // [!code word:foo]
(optional // [!code word:foo:2], um die Anzahl auf die nächsten N Zeilen zu begrenzen) umschließt jedes Vorkommen von foo mit einem Span, der highlighted-word enthält.
// [!code word:config]
const config = loadConfig()
Metadaten-Transformer
Diese lesen den Metadaten-String des Code-Fences, der über die Option
meta bereitgestellt wird (meta['__raw']) – so wie Shiki den Text nach der Sprache eines abgegrenzten Codeblocks ausliest.
MetaHighlight
Shikiphp\Transformer\MetaHighlight — hebt die Zeilen hervor, die in einem Metadatenbereich wie {1,3-5} angegeben sind, und fügt die Klasse highlighted hinzu.
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\MetaHighlight;
$code = "const a = 1\nconst b = 2\nconst c = 3\nconst d = 4\nconst e = 5";
echo Shikiphp::codeToHtml($code, [
'lang' => 'js',
'theme' => 'nord',
'meta' => ['__raw' => '{1,3-5}'],
'transformers' => [new MetaHighlight()],
]);
MetaWordHighlight
Shikiphp\Transformer\MetaWordHighlight — hebt Wörter hervor, die im Metadaten-String mit der Syntax /word/ angegeben sind, und fügt highlighted-word hinzu.
Leerraum und Struktur
RenderWhitespace
Shikiphp\Transformer\RenderWhitespace — umschließt Leerzeichen und Tabulatoren mit Spans (standardmäßig die Klassen space und tab), damit sie sichtbar dargestellt werden können.
Der Konstruktor erhält (classSpace, classTab, position), wobei position 'all' (Standard), 'boundary', 'trailing' oder 'leading' ist.
CompactLineOptions
Shikiphp\Transformer\CompactLineOptions — wendet Klassen auf bestimmte Zeilen an, basierend auf einer Liste von {line, classes}-Einträgen.
use Shikiphp\Transformer\CompactLineOptions;
$transformer = new CompactLineOptions([
['line' => 1, 'classes' => 'highlighted'],
['line' => 3, 'classes' => ['highlighted', 'error']],
]);
Zeilennummern beginnen bei 1. Übergib das Objekt wie jeden anderen Transformer über die Option transformers.
RemoveNotationEscape
Shikiphp\Transformer\RemoveNotationEscape — hebt die Escape-Sequenz [\!code …] auf, mit der du einen wörtlichen Notationskommentar schreiben kannst, ohne einen Notations-Transformer auszulösen.
RemoveLineBreak
Shikiphp\Transformer\RemoveLineBreak — entfernt die Zeilenumbruch-Textknoten zwischen den Zeilen (Shikis transformerRemoveLineBreak).
StyleToClass
Shikiphp\Transformer\StyleToClass — verschiebt Inline-Stile in generierte Klassen
und sammelt ein Stylesheet, das du mit getCSS() abrufst. Klassennamen hashen den Stil
mit cyrb53 von Shiki, sodass die Ausgabe byteidentisch mit @shikijs/transformers
4.2.0 ist, einschließlich des Geltungsbereichs:
- Der Stil
<pre>wird immer zu einer Klasse. - Token-Spans werden nur dann umgewandelt, wenn sie einen vorgefertigten Stil enthalten, wie bei einer
themes-Map. Bei einem einzelnenthemebehalten die Token-Spans ihre Inline-style="color:…", wie in Shiki.
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\StyleToClass;
$code = 'const answer = 42';
$styleToClass = new StyleToClass();
$html = Shikiphp::codeToHtml($code, [
'lang' => 'js',
'themes' => ['light' => 'github-light', 'dark' => 'github-dark'],
'transformers' => [$styleToClass],
]);
$css = $styleToClass->getCSS(); // collected stylesheet for the classes above
Der Konstruktor erhält (classPrefix, classSuffix, classReplacer); das Präfix ist standardmäßig __shiki_.