PHP SDK für Dateikonvertierung#

enconvert/enconvert-php ist der offizielle PHP-Client für die EnConvert API. Installiere ihn mit Composer, gib ihm einen API-Key, und du bekommst zwei Dinge: zwölf Konvertierungsmethoden, die URLs, Bilder und Dokumente in PDFs, PNGs, Markdown und Datenformate verwandeln, sowie einen $client->v2-Namensraum, der das lebende Web in agentenfertiges Markdown, Screenshots und strukturiertes JSON liest. Das SDK zielt auf PHP 8.1+, basiert auf Guzzle 7, nutzt durchgehend camelCase-Options-Arrays und gibt readonly typisierte Ergebnisobjekte statt loser Arrays zurück. Lange Konvertierungen, die ein HTTP-Timeout überdauern, werden per Job-Polling automatisch aufgefangen.

Composer: enconvert/enconvert-php · Quelle: conversionapi/php-sdk · PHP: 8.1+ · Benötigt: guzzlehttp/guzzle ^7.8

Installation#

composer require enconvert/enconvert-php

Das Paket lädt automatisch unter dem PSR-4-Namensraum Enconvert\ und bringt kein CLI, keine Konfigurationsdatei und keinen Service Provider mit. Es funktioniert unverändert in reinem PHP, Laravel, Symfony, WordPress und jedem PSR-4-Projekt.


Schnellstart#

<?php

require __DIR__ . '/vendor/autoload.php';

use Enconvert\Client;

$client = new Client(getenv('ENCONVERT_API_KEY'));

// Eine Live-Seite in ein PDF konvertieren und direkt auf die Festplatte streamen.
$result = $client->convertUrlToPdf('https://example.com', [
    'saveTo' => 'page.pdf',
]);

echo $result->presignedUrl, ' ', $result->fileSize, ' bytes', PHP_EOL;

// Das Lesen einer Seite für einen Agenten nutzt denselben Client und denselben Key.
$op = $client->v2->perceive('https://example.com', ['outputs' => ['markdown', 'structured']]);

echo $op->renderQuality, PHP_EOL;              // 0.0 bis 1.0, z. B. 0.93
echo $op->outputs['markdown']->url, PHP_EOL;   // signierte Download-URL

Das SDK ist ausschließlich serverseitig. Ein privater Key (sk_live_...) darf niemals in einen Browser oder eine Mobile-App gelangen.


Was der Client bereitstellt#

Oberfläche Wie du sie erreichst Was sie tut
Datei- und URL-Konvertierung $client->convert*() Zwölf Methoden über POST /v1/convert/*, plus Job- und Batch-Polling.
Web-Intelligence (V2) $client->v2 (oder $client->v2()) Dreiundzwanzig Methoden über /v2/*: perceive, discover, lookup, distill, ingest, watch.
Format-Introspektion Enconvert\Formats Die 43 implementierten {input}-to-{output}-Paare, MIME-Auflösung und Ausgabelisten pro Eingabe.
Fehler Enconvert\Exception\* Basisklasse EnconvertException plus ApiException, AuthenticationException, QuotaException, RateLimitException.
Ergebnisse Enconvert\Model\* Readonly Value Objects mit camelCase-Eigenschaften.

$client->v2 ist eine öffentliche readonly-Eigenschaft. $client->v2() ist ein identischer Zugriff für alle, die Methodensyntax bevorzugen. Jeder V2-Endpunkt verlangt einen privaten API-Key; öffentliche Keys werden abgelehnt.


Datei-Konvertierung#

Jede Einzeldatei-Methode gibt ein ConversionResult mit einer vorsignierten Download-URL zurück. Übergibst du saveTo, streamt das SDK die Bytes zusätzlich an diesen lokalen Pfad und legt dabei übergeordnete Verzeichnisse an.

convertUrlToPdf#

Rendere jede erreichbare URL zu einem PDF.

$result = $client->convertUrlToPdf('https://example.com', [
    'singlePage' => false,
    'pdfOptions' => ['pageSize' => 'A4', 'orientation' => 'landscape'],
    'viewportWidth' => 1440,
    'saveTo' => 'report.pdf',
]);
Option Typ Standard Beschreibung
saveTo string keiner Lokaler Pfad, in den das PDF gestreamt wird. Übergeordnete Verzeichnisse werden angelegt.
singlePage bool true true erzeugt eine einzige fortlaufende Seite. false paginiert anhand von pdfOptions.pageSize.
pdfOptions array keiner Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopf- und Fußzeile. Siehe PDF-Optionen.
viewportWidth, viewportHeight int 1920, 1080 Größe des Browser-Viewports in Pixeln.
loadMedia bool true Vor der Erfassung auf Bilder und Videos warten.
enableScroll bool true Von oben nach unten scrollen, damit Lazy-Loader auslösen.
outputFilename string auto Überschreibt den generierten Dateinamen.
auth, cookies, headers array keiner Seitenzugriff: Basic Auth ['username' => ..., 'password' => ...], eingeschleuste Cookies, eigene Request-Header.
Kombiniere auth nicht mit einem Authorization-Header. Die API lehnt diesen Konflikt ab, statt zu raten, welche Zugangsdaten gemeint sind.

convertUrlToScreenshot#

Erfasse ein PNG einer beliebigen URL.

$shot = $client->convertUrlToScreenshot('https://example.com', [
    'viewportWidth' => 1440,
    'saveTo' => 'shot.png',
]);

Akzeptiert dieselben Optionen für Viewport, Medien, Scrollen, Dateiname und Seitenzugriff wie convertUrlToPdf, ohne singlePage und pdfOptions.

convertUrlToMarkdown#

Extrahiere sauberes GitHub-Flavored Markdown aus einer URL. Navigation, Fußzeilen, Werbung und Skripte werden entfernt, der Haupttext des Artikels bleibt erhalten, und YAML-Frontmatter (Titel, Beschreibung, URL, Links, Bilder) wird vorangestellt.

$md = $client->convertUrlToMarkdown('https://example.com/article', [
    'saveTo' => 'article.md',
]);

Gleicher Optionssatz wie bei convertUrlToScreenshot.

convertImage#

Konvertiere zwischen jpeg, png, svg, heic und webp oder rastere ein PDF zu JPEG.

// Von einem Pfad.
$client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']);

// Aus rohen Bytes. Der Dateiname löst Eingabeformat und MIME-Typ auf.
$client->convertImage(
    ['data' => file_get_contents('photo.heic'), 'filename' => 'photo.heic'],
    ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']
);

$client->convertImage('scan.pdf', ['outputFormat' => 'jpeg', 'saveTo' => 'scan.jpeg']); // rastern

Das Eingabeformat ergibt sich aus der Dateiendung: .jpg, .jpeg, .png, .svg, .heic, .webp und .pdf. Das Ausgabeformat ist erforderlich und wird für dich normalisiert, jpg wird also zu jpeg aufgelöst.

Option Typ Erforderlich Beschreibung
outputFormat string Ja Eines von jpeg, png, svg, heic, webp.
saveTo string Nein Lokaler Pfad, in den das Ergebnis gestreamt wird.
outputFilename string Nein Überschreibt den generierten Dateinamen.

convertDocument#

Konvertiere Dokumente und Datenformate. outputFormat ist standardmäßig pdf.

// docx zu pdf, dann json zu yaml
$client->convertDocument('report.docx', ['saveTo' => 'report.pdf']);
$client->convertDocument('data.json', ['outputFormat' => 'yaml', 'saveTo' => 'data.yaml']);

// markdown zu pdf mit Seiteneinrichtung
$client->convertDocument('README.md', [
    'outputFormat' => 'pdf',
    'pdfOptions' => ['pageSize' => 'A4', 'margins' => ['top' => 20, 'bottom' => 20]],
    'saveTo' => 'readme.pdf',
]);

Erkannte Eingabe-Erweiterungen: .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.

Option Typ Standard Beschreibung
outputFormat string "pdf" Zielformat. Die Aliasse jpg, yml, htm und md werden aufgelöst.
saveTo string keiner Lokaler Pfad, in den das Ergebnis gestreamt wird.
outputFilename string keiner Überschreibt den generierten Dateinamen.
pdfOptions array keiner Seiteneinrichtung. Wird berücksichtigt, wenn die Ausgabe ein PDF ist.

EPUB hat kein eigenes Dokumentpaar. Schicke .epub-Dateien durch convertToPdf oder convertToMarkdown.

Unterstützte Konvertierungspaare#

Eingabe Ausgaben
json csv, toml, xml, yaml
xml csv, json
yaml, toml json
csv json, xml
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp untereinander (alle 20 Paare)
pdf jpeg

Das SDK prüft das {input}-to-{output}-Paar gegen diese Tabelle und wirft eine EnconvertException, bevor eine Anfrage deinen Prozess verlässt, samt der Liste der gültigen Ausgaben für diese Eingabe in der Meldung. Du kannst dieselbe Tabelle direkt abfragen:

use Enconvert\Formats;

Formats::validOutputsFor('json');   // ["csv", "toml", "xml", "yaml"]
Formats::validOutputsFor('pdf');    // ["jpeg"]
Formats::normalizeOutputFormat('JPG');   // "jpeg"
Formats::mimeFor('deck.pptx');           // der PPTX-MIME-Typ
count(Formats::IMPLEMENTED_CONVERSIONS); // 43

convertToMarkdown#

Erkenne das Format eines Dokuments serverseitig automatisch und erhalte Markdown zurück. Das ist der Baustein für die RAG-Aufnahme von Dateien, die du bereits auf der Festplatte hast.

$client->convertToMarkdown('handbook.docx', ['saveTo' => 'handbook.md']);

Akzeptiert PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD sowie ältere oder ODF-Office-Dateien. Bilder werden nicht unterstützt. Die Optionen sind saveTo und outputFilename; an diesem Endpunkt gibt es keine PDF-Optionen.

convertToPdf#

Erkenne fast jede Eingabe automatisch und erhalte ein PDF zurück.

$client->convertToPdf('slides.pptx', ['saveTo' => 'slides.pdf']);

// Eine PDF-Eingabe wird durchgereicht, das dient also zugleich der Graustufen-Normalisierung.
$client->convertToPdf('scan.pdf', ['pdfOptions' => ['grayscale' => true], 'saveTo' => 'gray.pdf']);

Akzeptiert Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, reinen Text, Rasterbilder, SVG, EPUB und ein bestehendes PDF als Durchreichung.

Nur pdfOptions.grayscale wird hier berücksichtigt. Das Format wird serverseitig erkannt, die Seitengeometrie kommt also aus dem Quelldokument. Nimm convertDocument oder convertUrlToPdf, wenn du Seitengröße, Ränder, Ausrichtung, Kopf- oder Fußzeilen brauchst.

convertWebsiteToPdf und convertWebsiteToScreenshot#

Ermittle jede Seite einer Website, konvertiere jede einzelne im Hintergrund und sammle alles in einem einzigen ZIP. Beide arbeiten asynchron und geben ein BatchSubmission statt eines ConversionResult zurück. Sie benötigen einen privaten API-Key mit Crawl-Zugriff.

$batch = $client->convertWebsiteToPdf('https://example.com', [
    'crawlMode' => 'sitemap',              // "auto" (Standard) | "sitemap" | "full"
    'excludePatterns' => ['/blog/tag/'],   // nur im Full-Crawl-Modus
    'notificationEmail' => '[email protected]',
]);

echo $batch->batchId, ' ', $batch->urlCount, ' ', $batch->discoveryMethod, PHP_EOL;

// Blockiert, bis der Batch "processing" verlässt, dann das ZIP speichern.
$status = $client->waitForBatch($batch->batchId, ['saveTo' => 'site.zip']);
echo $status->completed, ' of ', $status->total, ' pages converted', PHP_EOL;

// Oder frage ihn selbst mit getBatchStatus() ab.
$s = $client->getBatchStatus($batch->batchId);
echo $s->status === 'processing' ? 'still working' : $s->zipDownloadUrl, PHP_EOL;

convertWebsiteToScreenshot nimmt dieselben Optionen ohne singlePage und pdfOptions und erzeugt ein ZIP mit PNGs.

Option Typ Standard Beschreibung
crawlMode string "auto" auto, sitemap oder full.
includePatterns, excludePatterns string[] keiner URLs nach Muster behalten oder verwerfen. Ausschlüsse greifen im Full-Crawl-Modus.
notificationEmail, callbackUrl string keiner E-Mail-Adresse und Webhook, die bei Abschluss benachrichtigt werden.
viewportWidth, viewportHeight, loadMedia, enableScroll gemischt Server-Standard Render-Optionen pro Seite. Werden nur gesendet, wenn du sie setzt.
auth, cookies, headers array keiner Seitenzugriff für Websites hinter einem Login.
outputFilename string keiner Überschreibt den ZIP-Dateinamen.

waitForBatch akzeptiert intervalMs (Standard 5000), timeoutMs (Standard 1800000, also 30 Minuten) und saveTo. Es wirft eine ApiException mit Status 504, wenn die Frist verstreicht, während der Batch noch läuft.

getJobStatus#

Frage einen einzelnen asynchronen oder wiederhergestellten Konvertierungsjob ab.

$status = $client->getJobStatus('job_abc123');

if ($status->status === 'success') {
    echo $status->presignedUrl, PHP_EOL;
} elseif ($status->status === 'failed') {
    echo $status->error, PHP_EOL;
}

Gibt einen JobStatus mit status (processing, success oder failed), presignedUrl, objectKey und error zurück. Du brauchst ihn selten direkt, weil das SDK für dich abfragt. Siehe Timeout-Recovery.


Web-Intelligence (V2)#

Der Namensraum $client->v2 verwandelt lebende Webseiten in agentenfertige Daten: rendern, suchen, extrahieren, aufnehmen und überwachen. Jeder Lesevorgang trägt renderQuality, einen Wert von 0.0 bis 1.0, der ein echtes Rendering von einem gescheiterten unterscheidet. Eine Challenge-Seite, eine Cookie-Wall, ein Login-Bildschirm oder eine leere SPA-Hülle kommt mit niedrigem Wert und gefüllten warnings zurück, sodass ein schlechter Lesevorgang nie unbemerkt in den Kontext eines Agenten gelangt. Der Inhalt wird trotzdem zurückgegeben: Er ist markiert, nicht versteckt. Die Konzepte findest du in der V2-Übersicht.

Perceive#

Rendere eine URL und erzeuge aus diesem einen Rendering alle Ausgaben, die du angefordert hast. Siehe die Perceive-Referenz.

$op = $client->v2->perceive('https://example.com/pricing', [
    'outputs' => ['markdown', 'screenshot', 'structured'],
    'extract' => ['tables', 'metadata'],
    'onlyMainContent' => true,
    'waitFor' => '.price',
]);

echo $op->renderQuality, ' ', $op->statusCode, PHP_EOL;  // Score, HTTP-Status der Quelle
print_r($op->deductions);                       // z. B. ["login_wall" => 0.65]
echo $op->outputs['markdown']->url, PHP_EOL;    // signierte URL, 15 Minuten
print_r($op->structured);

// Signierte URLs laufen ab. Signiere sie später neu, ohne erneut zu rendern.
$again = $client->v2->getPerceiveOperation($op->operationId);
Option Typ Beschreibung
outputs string[] Beliebige aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract string[] Strukturierte Felder, die gezogen werden, zum Beispiel metadata, structured_data, headings, tables, main_content, all.
schema array Schema für die LLM-gestützte strukturierte Extraktion.
onlyMainContent bool Entfernt das Seitengerüst aus der Markdown-Ausgabe.
waitFor, waitTimeoutMs string, int Nach der Navigation auf einen CSS-Selektor oder einen JS-Ausdruck warten.
jsCode string JavaScript, das nach der Navigation auf der Seite ausgeführt wird.
viewport, mobile, blockResources array, bool, string[] Render-Geometrie und Ressourcentypen, die vor dem Laden abgebrochen werden.
cacheMode string enabled, bypass oder refresh.
headers, cookies, auth array Seitenzugriff für authentifizierte Seiten.
respectRobots bool Lehnt URLs ab, die robots.txt verbietet.
pdfOptions array Seiteneinrichtung für die pdf-Ausgabe.
directDownload bool Gibt die rohen Artefakt-Bytes zurück. Nur von perceive() akzeptiert.
proxyUrl, geolocation, actionChain gemischt Vom SDK serialisiert, von der API reserviert.

Batch-Perceive#

$batch = $client->v2->perceiveBatch(['https://a.com', 'https://b.com'], [
    'outputs' => ['markdown'],
    'outputMode' => 'zip',
]);

// Kleine Batches werden inline fertig. Größere kommen als queued zurück, frage sie also ab.
if ($batch->status !== 'completed') {
    $batch = $client->v2->getPerceiveBatch($batch->jobId);
}
foreach ($batch->items as $item) {
    echo $item->url, ' ', $item->renderQuality, PHP_EOL;
}
echo $batch->zip?->url, PHP_EOL;

Bis zu 1000 URLs teilen sich einen Options-Block. outputMode ist manifest (Standard) oder zip. directDownload wird hier nicht akzeptiert.

Ein einzelnes Artefakt streamen#

perceiveDirect() überspringt den JSON-Envelope und reicht dir die Bytes. Es muss genau eine artefakterzeugende Ausgabe angefordert werden, und das SDK erzwingt das vor dem Senden, structured allein wirft also eine Exception.

$direct = $client->v2->perceiveDirect('https://example.com', [
    'outputs' => ['pdf'],
]);

file_put_contents($direct->filename ?? 'page.pdf', $direct->content);
echo $direct->renderQuality, ' ', $direct->contentType, PHP_EOL;
echo $direct->operationId, ' cache hit: ', var_export($direct->cacheHit, true), PHP_EOL;

// Ein gespeichertes Artefakt später erneut herunterladen. Lass den Ausgabenamen weg,
// wenn die Operation genau ein Artefakt erzeugt hat.
$saved = $client->v2->downloadPerceiveArtifact($direct->operationId, 'pdf');

PerceiveDirectResult trägt content, contentType, filename, operationId, objectKey, cacheHit, renderQuality, sourceStatusCode, contentHash und warningsCount. Eine ApiException mit Status 410 bedeutet, dass das gespeicherte Artefakt nicht mehr verfügbar ist.

Discover#

Zähle die URLs einer Website ausschließlich über HTTP auf. Kein Browser-Rendering, daher ist es schnell. Siehe die Discover-Referenz.

$found = $client->v2->discover('https://example.com', [
    'mode' => 'hybrid',                 // "sitemap" | "crawl" | "hybrid"
    'maxUrls' => 200,
    'maxDepth' => 3,
    'excludePatterns' => ['/tag/'],
    'sameDomainOnly' => true,
]);

echo $found->total, ' urls, truncated: ', var_export($found->truncated, true), PHP_EOL;
print_r($found->sources);   // z. B. ["sitemap" => 42, "crawl" => 30]

Die Optionen sind mode, maxUrls, maxDepth, includePatterns, excludePatterns, sameDomainOnly und respectRobots.

Lookup#

Führe eine kategorisierte Websuche aus und rendere die besten Treffer auf Wunsch automatisch. Siehe die Lookup-Referenz.

$search = $client->v2->lookup('best static site generators', [
    'category' => 'web',        // web | news | images | scholar | patents | maps
    'numResults' => 10,
    'country' => 'us',
    'timeFilter' => 'month',
    'perceiveTop' => 3,         // die 3 besten Treffer automatisch rendern
]);

foreach ($search->results as $hit) {
    echo $hit->position, '. ', $hit->title, ' ', $hit->url, PHP_EOL;
    echo '   quality ', $hit->perceive?->renderQuality ?? 'not perceived', PHP_EOL;
}

Die Optionen sind category, country, locale, timeFilter, numResults, page, location, autocorrect und perceiveTop.

Distill#

Schemagesteuerte strukturierte Extraktion über eine URL-Liste oder eine ermittelte Website. Siehe die Distill-Referenz.

$extraction = $client->v2->distill([
    'urls' => ['https://example.com/pricing'],
    'schema' => ['plans' => 'list of plan names with monthly prices'],
    'cssSchema' => [                     // optionaler kostenloser CSS-Durchlauf vor der LLM-Stufe
        'baseSelector' => '.plan-card',
        'fields' => [
            ['name' => 'name', 'type' => 'text', 'selector' => 'h3'],
            ['name' => 'price', 'type' => 'text', 'selector' => '.price'],
        ],
    ],
]);

print_r($extraction->results[0]->data);
echo $extraction->results[0]->extractionTier, PHP_EOL;  // css | llm | mixed | none
echo $extraction->results[0]->fieldsFromCss, '/', $extraction->results[0]->fieldsFromLlm, PHP_EOL;

// Oder ermittle zuerst die URL-Menge.
$client->v2->distill([
    'discoverFrom' => ['url' => 'https://example.com', 'mode' => 'sitemap', 'maxPages' => 10],
    'schema' => ['title' => 'page title', 'summary' => 'one-line summary'],
]);

schema ist erforderlich, und genau eines von urls oder discoverFrom muss vorhanden sein. Verletzt du eine der beiden Regeln, wirft das SDK vor dem Senden eine EnconvertException. Weitere Optionen sind waitFor, waitTimeoutMs, headers, cookies und respectRobots.

Ingest#

Verwandle eine ganze Website oder einen Stapel hochgeladener Dokumente über eine einzige Pipeline in gechunktes, RAG-fertiges JSONL. Ingest arbeitet immer asynchron. Siehe die Ingest-Referenz.

// Von einer Website.
$job = $client->v2->ingest([
    'mode' => 'sitemap',                 // "urls" (Standard) | "sitemap" | "crawl"
    'url' => 'https://docs.example.com',
    'maxPages' => 100,
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
    'webhookUrl' => 'https://my.app/hooks/enconvert',
]);

// Oder aus hochgeladenen Dateien: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, ältere und ODF-Office.
$fileJob = $client->v2->ingestFiles(['handbook.pdf', 'notes.docx'], [
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
]);

// Abfragen, bis es fertig ist.
do {
    sleep(5);
    $status = $client->v2->getIngestJob($job->jobId);
} while (in_array($status->status, ['queued', 'discovering', 'processing'], true));

if ($status->status === 'completed') {
    echo $status->totalChunks, ' chunks: ', $status->outputUrl, PHP_EOL;  // signiertes JSONL
}

mode ist standardmäßig urls, was ein nicht leeres urls-Array verlangt und url ablehnt. Die Modi sitemap und crawl verlangen eine Start-url und lehnen urls ab. Das SDK erzwingt beide Regeln lokal. Weitere Optionen sind maxPages, maxDepth, sameDomainOnly, includePatterns, excludePatterns, respectRobots, waitFor, waitTimeoutMs, chunk und webhookUrl.

// Job-Verwaltung.
$list = $client->v2->listIngestJobs(['limit' => 20, 'skip' => 0]);
foreach ($list->jobs as $summary) {
    echo $summary->jobId, ' ', $summary->status, ' ', $summary->totalChunks, PHP_EOL;
}
$client->v2->cancelIngestJob($job->jobId);        // idempotent

// Webhook-Signierung.
$secret = $client->v2->getWebhookSecret();
echo $secret->signatureHeader, ' ', $secret->signatureScheme, PHP_EOL;
$client->v2->rotateWebhookSecret();               // alte Signaturen verifizieren nicht mehr
$client->v2->retryIngestWebhook($job->jobId);     // einen Abschluss-Callback erneut zustellen

Watch#

Rendere eine URL in festem Takt erneut und lass dich benachrichtigen, wenn sie sich ändert. Siehe die Watch-Referenz.

$watcher = $client->v2->createWatcher('https://example.com/pricing', [
    'frequencyMinutes' => 60,            // Untergrenze eine Stunde
    'diffMode' => 'auto',                // auto | text | structured | tables | metadata
    'webhookUrl' => 'https://my.app/hooks/changes',
    'notifyEmail' => true,
]);

echo $watcher->watcherId, ' next check ', $watcher->nextCheckAt, PHP_EOL;

$snapshots = $client->v2->getWatcherSnapshots($watcher->watcherId, ['limit' => 10]);
foreach ($snapshots->snapshots as $snap) {
    if ($snap->hasChanges) {
        echo $snap->checkedAt, ' similarity ', $snap->similarity,
             ' changes ', $snap->changeCount, PHP_EOL;
    }
}
$client->v2->updateWatcher($watcher->watcherId, ['status' => 'paused']);
$client->v2->updateWatcher($watcher->watcherId, ['webhookUrl' => '']);  // löscht den Webhook
$client->v2->deleteWatcher($watcher->watcherId);                        // Soft Delete, idempotent

createWatcher akzeptiert frequencyMinutes, diffMode, trackFields, webhookUrl und notifyEmail. updateWatcher akzeptiert diese fünf plus status und verlangt mindestens ein Feld, sonst wirft es eine EnconvertException. listWatchers akzeptiert skip und limit.

Snapshot-Diffs enthalten nicht vertrauenswürdige Seiteninhalte. Die `changes`-Einträge eines `WatcherSnapshot` stammen aus der überwachten Seite. Escape sie mit `htmlspecialchars()`, bevor du sie in deiner eigenen Oberfläche darstellst.

PDF-Optionen#

Werden als pdfOptions-Array an convertUrlToPdf, convertDocument, convertToPdf (nur Graustufen), convertWebsiteToPdf und V2-perceive mit einer pdf-Ausgabe übergeben. Das SDK serialisiert camelCase-Schlüssel in das Wire-Format der API und sendet nur die Schlüssel, die du setzt.

$client->convertUrlToPdf('https://example.com', [
    'pdfOptions' => [
        'pageSize' => 'A4',
        'orientation' => 'landscape',
        'margins' => ['top' => 10, 'bottom' => 10, 'left' => 15, 'right' => 15],
        'scale' => 0.9,
        'header' => ['content' => 'Quarterly Report', 'height' => 15],
        'footer' => ['content' => 'Page {{page}} of {{total_pages}}', 'height' => 12],
    ],
    'singlePage' => false,
    'saveTo' => 'report.pdf',
]);
Feld Typ Beschreibung
pageSize string A0 bis A6, B0 bis B5, Letter, Legal, Tabloid, Ledger. Wird ignoriert, wenn sowohl pageWidth als auch pageHeight gesetzt sind.
pageWidth, pageHeight float Eigene Seitengröße in Millimetern. Setze beide gemeinsam.
orientation string portrait oder landscape.
margins array ['top' => ..., 'bottom' => ..., 'left' => ..., 'right' => ...] in Millimetern.
scale float Render-Skalierung, 0.1 bis 2.0. Nur bei paginierter Ausgabe.
grayscale bool Wandelt das PDF nachträglich in Graustufen um.
header array ['content' => '<html>', 'height' => 15]. Höhe in Millimetern.
footer array Gleiche Struktur wie header.

Inhalte von Kopf- und Fußzeile unterstützen die Vorlagenvariablen {{page}}, {{total_pages}}, {{date}}, {{title}} und {{url}}. Die vollständige Parameter-Referenz steht unter Parameter und Optionen.


Fehlerbehandlung#

Alles, was das SDK wirft, stammt von Enconvert\Exception\EnconvertException ab, ein einziger catch-Block kann also die gesamte Oberfläche abdecken.

use Enconvert\Exception\{
    ApiException, AuthenticationException, EnconvertException, QuotaException, RateLimitException
};

try {
    $client->convertUrlToPdf('https://example.com', ['saveTo' => 'page.pdf']);
} catch (AuthenticationException $e) {
    error_log('Check ENCONVERT_API_KEY: ' . $e->getMessage());
} catch (RateLimitException $e) {
    error_log('Too many requests, back off and retry.');
} catch (QuotaException $e) {
    error_log($e->getMessage());
} catch (ApiException $e) {
    error_log(sprintf('API error [%d]: %s', $e->getStatusCode(), $e->getMessage()));
} catch (EnconvertException $e) {
    error_log('Client-side or transport failure: ' . $e->getMessage());
}
Klasse Ausgelöst bei Statuscode
AuthenticationException Ungültiger, fehlender oder widerrufener API-Key Antworten mit 401 und 403
QuotaException HTTP 402 402
RateLimitException Zu viele Anfragen 429
ApiException Jede andere 4xx- oder 5xx-Antwort der tatsächliche Code
EnconvertException Basisklasse und jeder clientseitige Fehler keiner

ApiException::getStatusCode() gibt den numerischen Status zurück; der Meldung ist [code] vorangestellt. Eine 403-Antwort wird auf AuthenticationException abgebildet, deren getStatusCode() 401 meldet: Verzweige also über die Klasse statt über die Zahl, wenn du beide unterscheiden musst.

EnconvertException wird auch ohne Netzwerk-Roundtrip geworfen, sobald das SDK erkennt, dass die Anfrage aussichtslos ist: ein leerer API-Key, ein fehlender Dateipfad, eine unbekannte Dateiendung, ein nicht implementiertes Konvertierungspaar, ein distill-Aufruf ohne Schema oder mit urls und discoverFrom zugleich, ein Widerspruch zwischen ingest-Modus und Payload, eine leere ingestFiles-Liste, ein perceiveDirect-Aufruf, der nicht genau eine Artefakt-Ausgabe benennt, ein updateWatcher-Aufruf ohne Felder sowie jeder Guzzle-Transportfehler (gemeldet als HTTP request failed: ...).

Die vollständige Zuordnung der Meldungen steht in der Fehlercodes-Referenz.


Timeout-Recovery#

Große Dokumentkonvertierungen und langsame Seiten können ein Reverse-Proxy-Timeout überdauern, selbst wenn die Konvertierung auf dem Server letztlich erfolgreich ist. Das SDK fängt das von allein auf:

  1. Vor jeder Konvertierungsanfrage erzeugt es eine 32 Zeichen lange Hex-Job-ID und sendet sie als job_id im Body oder im Multipart-Formular.
  2. Kommt diese Anfrage mit 5xx zurück, wirft das SDK keine Exception mehr, sondern beginnt, GET /v1/convert/status/{job_id} alle 3 Sekunden abzufragen.
  3. Sobald der Job success meldet, gibt das SDK das Ergebnis zurück. Meldet er failed, wirft es eine ApiException mit der Fehlermeldung des Servers.
  4. Ein 404 beim Abfragen bedeutet "noch nicht erfasst", und das Polling läuft weiter. Die Frist beträgt 5 Minuten, danach wirft es ApiException(504, 'Conversion timed out').

Dafür ist kein Code nötig. Zwei bewusste Ausnahmen: Die Website-Batch-Übermittlungen (convertWebsiteToPdf, convertWebsiteToScreenshot) haben keine Job-Zeile, ein 5xx wird dort also sofort gemeldet, und V2-Methoden nutzen überhaupt kein Job-Polling.

Erfolgreiche Antworten, die job_id weglassen, werden mit der vom Client erzeugten ID ergänzt, $result->jobId ist also immer mit getJobStatus() verwendbar.


Konfiguration#

use Enconvert\Client;

$client = new Client(getenv('ENCONVERT_API_KEY'), [
    'timeout' => 300,                            // Sekunden
    'base_url' => 'https://api.enconvert.com',   // Überschreiben für ein selbst gehostetes Gateway
]);
Option Typ Standard Beschreibung
$apiKey (erstes Argument) string erforderlich Privater API-Key (sk_live_...). Ein leerer String wirft eine EnconvertException.
timeout int\|float 300 Anfrage-Timeout in Sekunden, der bei Guzzle übliche Wert. Gilt für jede HTTP-Anfrage des Clients. Die Frist für das Job-Polling ist davon getrennt und fest auf 300 Sekunden gesetzt.
base_url string https://api.enconvert.com API-Host. Abschließende Schrägstriche werden entfernt.

Beachte, dass der Options-Schlüssel base_url in snake_case steht, während jede andere Request-Option im SDK in camelCase geschrieben wird. Anfragen werden mit einem X-API-Key-Header authentifiziert; saveTo-Downloads gehen direkt an den Speicher und senden bewusst keinen Key, weil die URL bereits signiert ist.

Schreibe den API-Key niemals fest in den Code. Lies ihn aus einer Umgebungsvariablen oder deinem Secret-Manager und halte ihn aus der Versionsverwaltung und aus allem heraus, was an einen Browser ausgeliefert wird. Keys erzeugst und rotierst du im Dashboard, und die Key-Typen erklärt der Authentifizierungs-Leitfaden.

Ergebnisform#

Jede Einzeldatei-Konvertierung gibt ein Enconvert\Model\ConversionResult mit readonly öffentlichen Eigenschaften zurück:

final class ConversionResult
{
    public readonly string $presignedUrl;              // signierte Download-URL
    public readonly string $objectKey;                 // Objekt-Key im Speicher
    public readonly string $filename;                  // serverseitiger Dateiname
    public readonly int|float|null $fileSize;          // Bytes
    public readonly int|float|null $conversionTimeSeconds;
    public readonly ?string $jobId;                    // die ID für die Timeout-Recovery
}

Vorsignierte URLs laufen nach 15 Minuten ab und können bis dahin mehrfach abgerufen werden. Für dauerhaften Zugriff lade die Bytes herunter (übergib saveTo oder rufe die URL selbst ab) und lege sie in deinem eigenen Bucket ab.

Die übrigen Ergebnistypen folgen demselben Muster: JobStatus, BatchSubmission, BatchStatus mit seinen BatchItem[] und, unter Enconvert\Model\V2, PerceiveResult, PerceiveDirectResult, PerceiveBatchResult, OutputArtifact, DiscoverResult, LookupResult, LookupItem, DistillResult, DistillItem, IngestJob, IngestJobList, IngestJobSummary, Watcher, WatcherList, WatcherSummary, WatcherSnapshot, WatcherSnapshotList, WebhookSecret, WebhookRetryResult und Tokens. Wire-Felder kommen in snake_case an und werden auf camelCase-Eigenschaften abgebildet; von dir gelieferte Payloads wie Schemata, extrahierte Daten, überwachte Felder und Diff-Einträge werden unverändert durchgereicht.


Quelle und Issues#


Häufig gestellte Fragen#

Wie konvertiere ich Dateien in PHP mit Composer?#

Führe composer require enconvert/enconvert-php aus, erzeuge new Enconvert\Client($apiKey) und rufe eine Methode wie convertUrlToPdf, convertImage, convertDocument, convertToPdf oder convertToMarkdown auf. Ergänze bei jeder davon saveTo, und das SDK streamt die konvertierten Bytes für dich an diesen lokalen Pfad und legt dabei übergeordnete Verzeichnisse an.

Wie konvertiere ich DOCX in PHP in ein PDF?#

Rufe $client->convertDocument('report.docx', ['saveTo' => 'report.pdf']) auf. Das Ausgabeformat ist standardmäßig pdf, outputFormat ist also nicht nötig. Das Eingabeformat wird aus der Dateiendung gelesen, und das SDK prüft das Paar doc-to-pdf gegen seine Tabelle mit 43 implementierten Konvertierungen, bevor irgendetwas gesendet wird: Ein nicht unterstütztes Paar scheitert sofort mit einer Meldung, die auflistet, was für diese Eingabe gültig ist.

Wie konvertiere ich eine URL in PHP in ein PDF?#

Rufe $client->convertUrlToPdf('https://example.com', ['saveTo' => 'page.pdf']) auf. Standardmäßig wird die Seite als eine fortlaufende Seite bei einem Viewport von 1920x1080 gerendert, mit aktiviertem Laden von Medien und Scrollen. Setze singlePage auf false und übergib pdfOptions, wenn du echte Paginierung mit Seitengröße, Rändern, Kopf- und Fußzeilen willst.

Wie scrape ich in PHP eine Webseite und bekomme sauberes Markdown?#

Nutze den V2-Namensraum: $client->v2->perceive($url, ['outputs' => ['markdown', 'structured']]). Er rendert die Seite in einem echten Browser, JavaScript-lastige Websites funktionieren also, und liefert eine signierte Markdown-URL plus inline strukturierte Daten. Prüfe renderQuality im Ergebnis, bevor du dem Inhalt vertraust; ein niedriger Wert mit gefüllten deductions bedeutet, dass das Rendering auf eine Anti-Bot-Seite, eine Login-Wall oder eine leere Hülle gestoßen ist. Für einen leichteren Einmalweg ohne V2-Funktionen funktioniert auch convertUrlToMarkdown.

Wie konvertiere ich HEIC in PHP in WebP?#

Rufe $client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']) auf. Jedes Paar aus jpeg, png, svg, heic und webp wird unterstützt, dazu die Rasterung von pdf zu jpeg. Du kannst auch rohe Bytes als ['data' => $bytes, 'filename' => 'photo.heic'] übergeben, wobei der Dateiname Eingabeformat und MIME-Typ auflöst.

Funktioniert das EnConvert PHP SDK mit Laravel oder Symfony?#

Ja. Das Paket ist eine schlichte PSR-4-Bibliothek mit Guzzle 7 als einziger Abhängigkeit und ohne Framework-Bindung, es passt also unverändert in Laravel, Symfony, WordPress oder ein einfaches Skript. Binde Enconvert\Client mit dem Key aus deiner Umgebungskonfiguration in deinen Container und injiziere ihn, wo du ihn brauchst.

Was passiert, wenn eine Konvertierung länger dauert als das HTTP-Timeout?#

Das SDK fängt das von allein auf. Es sendet mit jeder Konvertierungsanfrage eine vom Client erzeugte job_id, und wenn die Anfrage 5xx zurückgibt, fragt es still GET /v1/convert/status/{job_id} alle 3 Sekunden für bis zu 5 Minuten ab und gibt das Ergebnis zurück, sobald der Job success erfasst. Nach Ablauf der Frist wirft es ApiException(504, 'Conversion timed out'). Website-Batch-Übermittlungen nehmen daran nicht teil, da sie keine Job-Zeile haben.

Wie lange ist die vorsignierte Download-URL gültig?#

Vorsignierte Download-URLs laufen nach 15 Minuten ab und können bis dahin mehrfach verwendet werden. V2-Artefakt-URLs tragen aus demselben Grund ein expiresIn von 900 Sekunden, und getPerceiveOperation($operationId) signiert sie aus den gespeicherten Objekt-Keys neu, ohne die Seite erneut zu rendern. Für alles Dauerhafte lade die Bytes herunter und behalte sie in deinem eigenen Speicher.

Welche PHP-Version verlangt das SDK?#

PHP 8.1 oder neuer. Das SDK nutzt durchgehend readonly-Eigenschaften, enum-artige Union-Typen, benannte Argumente und match-Ausdrücke, 8.0 und älter werden also nicht unterstützt. Seine einzige Laufzeitabhängigkeit ist guzzlehttp/guzzle ^7.8.