---
seo_title: SDK PHP de conversion de fichiers (Composer) | EnConvert
meta_desc: SDK PHP officiel EnConvert pour PHP 8.1+. Installez-le via Composer pour convertir documents et images et pour perceive, discover, distill, ingest et watch.
keywords: sdk php conversion de fichiers, convertir des fichiers en php, url vers pdf php, api scraping web php, docx vers pdf php, enconvert php sdk, paquet composer conversion de fichiers, heic vers webp php, html vers pdf php composer, api url vers markdown php, conversion de documents laravel
---

# SDK PHP de conversion de fichiers

`enconvert/enconvert-php` est le client PHP officiel de l'API EnConvert. Installez-le avec Composer, donnez-lui une clé API, et vous obtenez deux choses : douze méthodes de conversion qui transforment des URL, des images et des documents en PDF, PNG, Markdown et formats de données, et un espace de noms `$client->v2` qui lit le web en direct sous forme de Markdown, de captures d'écran et de JSON structuré prêts pour un agent. Il cible PHP 8.1+, s'appuie sur Guzzle 7, utilise partout des tableaux d'options en camelCase, et renvoie des objets de résultat typés en lecture seule plutôt que des tableaux flottants. Les conversions longues qui dépassent un timeout HTTP sont récupérées automatiquement par interrogation du job.

<div class="alert alert-info">
<strong>Composer :</strong> <code>enconvert/enconvert-php</code> · <strong>Source :</strong> <a href="https://github.com/conversionapi/php-sdk">conversionapi/php-sdk</a> · <strong>PHP :</strong> 8.1+ · <strong>Requiert :</strong> <code>guzzlehttp/guzzle ^7.8</code>
</div>

---

## Installation

```bash
composer require enconvert/enconvert-php
```

Le package est autochargé sous l'espace de noms PSR-4 `Enconvert\` et n'embarque ni CLI, ni fichier de configuration, ni service provider. Il fonctionne tel quel en PHP nu, sous Laravel, Symfony, WordPress, et dans n'importe quel projet PSR-4.

---

## Démarrage rapide

```php
<?php

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

use Enconvert\Client;

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

// Convertir une page en direct en PDF et l'écrire directement sur le disque.
$result = $client->convertUrlToPdf('https://example.com', [
    'saveTo' => 'page.pdf',
]);

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

// Lire une page pour un agent passe par le même client et la même clé.
$op = $client->v2->perceive('https://example.com', ['outputs' => ['markdown', 'structured']]);

echo $op->renderQuality, PHP_EOL;              // de 0.0 à 1.0, par ex. 0.93
echo $op->outputs['markdown']->url, PHP_EOL;   // URL de téléchargement signée
```

Le SDK est côté serveur uniquement. Une clé privée (`sk_live_...`) ne doit jamais atteindre un navigateur ni une application mobile.

---

## Ce que le client expose

| Surface | Comment y accéder | Ce qu'elle fait |
|---------|------------------|--------------|
| Conversion de fichiers et d'URL | `$client->convert*()` | Douze méthodes sur `POST /v1/convert/*`, plus l'interrogation des jobs et des lots. |
| Web intelligence (V2) | `$client->v2` (ou `$client->v2()`) | Vingt-trois méthodes sur `/v2/*` : perceive, discover, lookup, distill, ingest, watch. |
| Introspection des formats | `Enconvert\Formats` | Les 43 paires `{input}-to-{output}` implémentées, la résolution MIME, et les listes de sorties par entrée. |
| Erreurs | `Enconvert\Exception\*` | La base `EnconvertException` plus `ApiException`, `AuthenticationException`, `QuotaException`, `RateLimitException`. |
| Résultats | `Enconvert\Model\*` | Objets valeur en lecture seule, avec des propriétés en camelCase. |

`$client->v2` est une propriété publique en lecture seule. `$client->v2()` est un accesseur identique pour ceux qui préfèrent la syntaxe méthode. Chaque endpoint V2 exige une clé API privée ; les clés publiques sont rejetées.

---

## Conversion de fichiers

Chaque méthode portant sur un fichier unique renvoie un `ConversionResult` porteur d'une URL de téléchargement présignée. Passez `saveTo` et le SDK écrit également les octets vers ce chemin local, en créant les répertoires parents au besoin.

### convertUrlToPdf

Rend en PDF n'importe quelle URL accessible.

```php
$result = $client->convertUrlToPdf('https://example.com', [
    'singlePage' => false,
    'pdfOptions' => ['pageSize' => 'A4', 'orientation' => 'landscape'],
    'viewportWidth' => 1440,
    'saveTo' => 'report.pdf',
]);
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `saveTo` | `string` | aucun | Chemin local vers lequel écrire le PDF. Les répertoires parents sont créés. |
| `singlePage` | `bool` | `true` | `true` produit une seule page continue. `false` pagine en utilisant `pdfOptions.pageSize`. |
| `pdfOptions` | `array` | aucun | Taille de page, orientation, marges, échelle, niveaux de gris, en-tête, pied de page. Voir [Options PDF](#options-pdf). |
| `viewportWidth`, `viewportHeight` | `int` | `1920`, `1080` | Taille de la fenêtre d'affichage du navigateur, en pixels. |
| `loadMedia` | `bool` | `true` | Attend les images et les vidéos avant la capture. |
| `enableScroll` | `bool` | `true` | Fait défiler la page de haut en bas pour déclencher les chargements différés. |
| `outputFilename` | `string` | auto | Remplace le nom de fichier généré. |
| `auth`, `cookies`, `headers` | `array` | aucun | Accès à la page : Basic Auth `['username' => ..., 'password' => ...]`, cookies injectés, en-têtes de requête personnalisés. |

<div class="alert alert-warning">
<strong>Ne combinez pas <code>auth</code> avec un en-tête <code>Authorization</code>.</strong> L'API rejette ce conflit plutôt que de deviner quel identifiant vous vouliez utiliser.
</div>

### convertUrlToScreenshot

Capture un PNG de n'importe quelle URL.

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

Accepte les mêmes options de fenêtre d'affichage, de médias, de défilement, de nom de fichier et d'accès à la page que `convertUrlToPdf`, à l'exception de `singlePage` et `pdfOptions`.

### convertUrlToMarkdown

Extrait du Markdown GitHub-Flavored propre à partir d'une URL. La navigation, les pieds de page, les publicités et les scripts sont supprimés, le corps principal de l'article est conservé, et un frontmatter YAML (title, description, url, links, images) est ajouté en tête.

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

Même jeu d'options que `convertUrlToScreenshot`.

### convertImage

Convertit entre `jpeg`, `png`, `svg`, `heic` et `webp`, ou rastérise un PDF en JPEG.

```php
// Depuis un chemin.
$client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']);

// Depuis des octets bruts. Le nom de fichier détermine le format d'entrée et le type MIME.
$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']); // rastérisation
```

Le format d'entrée vient de l'extension du fichier : `.jpg`, `.jpeg`, `.png`, `.svg`, `.heic`, `.webp` et `.pdf`. Le format de sortie est obligatoire et normalisé pour vous, si bien que `jpg` se résout en `jpeg`.

| Option | Type | Requis | Description |
|--------|------|----------|-------------|
| `outputFormat` | `string` | Oui | Une valeur parmi `jpeg`, `png`, `svg`, `heic`, `webp`. |
| `saveTo` | `string` | Non | Chemin local vers lequel écrire le résultat. |
| `outputFilename` | `string` | Non | Remplace le nom de fichier généré. |

### convertDocument

Convertit des documents et des formats de données. `outputFormat` vaut `pdf` par défaut.

```php
// docx vers pdf, puis json vers yaml
$client->convertDocument('report.docx', ['saveTo' => 'report.pdf']);
$client->convertDocument('data.json', ['outputFormat' => 'yaml', 'saveTo' => 'data.yaml']);

// markdown vers pdf avec mise en page
$client->convertDocument('README.md', [
    'outputFormat' => 'pdf',
    'pdfOptions' => ['pageSize' => 'A4', 'margins' => ['top' => 20, 'bottom' => 20]],
    'saveTo' => 'readme.pdf',
]);
```

**Extensions d'entrée reconnues :** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `outputFormat` | `string` | `"pdf"` | Format cible. Les alias `jpg`, `yml`, `htm` et `md` sont résolus. |
| `saveTo` | `string` | aucun | Chemin local vers lequel écrire le résultat. |
| `outputFilename` | `string` | aucun | Remplace le nom de fichier généré. |
| `pdfOptions` | `array` | aucun | Mise en page. Prise en compte lorsque la sortie est un PDF. |

L'EPUB n'a pas de paire de conversion documentaire dédiée. Faites passer les fichiers `.epub` par `convertToPdf` ou `convertToMarkdown`.

#### Paires de conversion prises en charge

| Entrée | Sorties |
|-------|---------|
| `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` | les uns vers les autres (les 20 paires) |
| `pdf` | `jpeg` |

Le SDK valide la paire `{input}-to-{output}` contre ce tableau et lève une `EnconvertException` avant qu'aucune requête ne quitte votre processus, avec la liste des sorties valides pour cette entrée dans le message. Vous pouvez interroger le même tableau directement :

```php
use Enconvert\Formats;

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

### convertToMarkdown

Détecte automatiquement le format d'un document côté serveur et renvoie du Markdown. C'est la brique d'ingestion RAG pour les fichiers que vous avez déjà sur le disque.

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

Accepte les formats PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, ainsi que les fichiers bureautiques hérités ou ODF. Les images ne sont pas prises en charge. Les options sont `saveTo` et `outputFilename` ; cet endpoint n'accepte aucune option PDF.

### convertToPdf

Détecte automatiquement presque n'importe quelle entrée et renvoie un PDF.

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

// Une entrée PDF est transmise telle quelle : cela sert donc aussi de normalisation en niveaux de gris.
$client->convertToPdf('scan.pdf', ['pdfOptions' => ['grayscale' => true], 'saveTo' => 'gray.pdf']);
```

Accepte les formats bureautiques, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ainsi qu'un PDF existant transmis tel quel.

<div class="alert alert-warning">
<strong>Seul <code>pdfOptions.grayscale</code> est pris en compte ici.</strong> Le format est détecté côté serveur, la géométrie de page vient donc du document source. Utilisez <code>convertDocument</code> ou <code>convertUrlToPdf</code> lorsque vous avez besoin d'une taille de page, de marges, d'une orientation, d'en-têtes ou de pieds de page.
</div>

### convertWebsiteToPdf et convertWebsiteToScreenshot

Découvrent chaque page d'un site, convertissent chacune d'elles en arrière-plan, et rassemblent le tout dans une seule archive ZIP. Les deux sont asynchrones et renvoient un `BatchSubmission` plutôt qu'un `ConversionResult`. Elles exigent une clé API privée disposant de l'accès au crawl.

```php
$batch = $client->convertWebsiteToPdf('https://example.com', [
    'crawlMode' => 'sitemap',              // "auto" (par défaut) | "sitemap" | "full"
    'excludePatterns' => ['/blog/tag/'],   // mode full crawl uniquement
    'notificationEmail' => 'ops@my.app',
]);

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

// Bloque jusqu'à ce que le lot quitte l'état "processing", puis enregistre le ZIP.
$status = $client->waitForBatch($batch->batchId, ['saveTo' => 'site.zip']);
echo $status->completed, ' of ', $status->total, ' pages converted', PHP_EOL;

// Ou interrogez-le vous-même avec getBatchStatus().
$s = $client->getBatchStatus($batch->batchId);
echo $s->status === 'processing' ? 'still working' : $s->zipDownloadUrl, PHP_EOL;
```

`convertWebsiteToScreenshot` prend les mêmes options, sans `singlePage` ni `pdfOptions`, et produit un ZIP de PNG.

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `crawlMode` | `string` | `"auto"` | `auto`, `sitemap` ou `full`. |
| `includePatterns`, `excludePatterns` | `string[]` | aucun | Conserve ou écarte des URL par motif. Les exclusions s'appliquent en mode full crawl. |
| `notificationEmail`, `callbackUrl` | `string` | aucun | Adresse e-mail et webhook à notifier à la fin du traitement. |
| `viewportWidth`, `viewportHeight`, `loadMedia`, `enableScroll` | variable | valeur serveur | Options de rendu par page. Envoyées uniquement si vous les définissez. |
| `auth`, `cookies`, `headers` | `array` | aucun | Accès aux pages des sites derrière une authentification. |
| `outputFilename` | `string` | aucun | Remplace le nom de fichier du ZIP. |

`waitForBatch` accepte `intervalMs` (`5000` par défaut), `timeoutMs` (`1800000` par défaut, soit 30 minutes) et `saveTo`. Elle lève une `ApiException` avec le statut `504` si le délai expire alors que le lot est toujours en traitement.

### getJobStatus

Interroge un job de conversion unique, asynchrone ou récupéré.

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

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

Renvoie un `JobStatus` avec `status` (`processing`, `success` ou `failed`), `presignedUrl`, `objectKey` et `error`. Vous en avez rarement besoin directement, puisque le SDK interroge pour vous. Voir [Récupération des timeouts](#recuperation-des-timeouts).

---

## Web intelligence (V2)

L'espace de noms `$client->v2` transforme des pages web en direct en données prêtes pour un agent : rendre, rechercher, extraire, ingérer et surveiller. Chaque lecture porte `renderQuality`, un score de 0.0 à 1.0 qui distingue un vrai rendu d'un rendu raté. Une page de challenge, un mur de cookies, un écran de connexion ou une coquille SPA vide reviennent avec un score bas et des `warnings` renseignés : une mauvaise lecture n'entre donc jamais silencieusement dans le contexte d'un agent. Le contenu est tout de même renvoyé ; il est signalé, pas masqué. Les concepts sont détaillés dans la [vue d'ensemble V2](/fr/docs/v2-overview).

### Perceive

Rend une URL et matérialise, à partir de ce rendu unique, toutes les sorties que vous avez demandées. Voir [la référence perceive](/fr/docs/v2-perceive).

```php
$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, statut HTTP amont
print_r($op->deductions);                       // par ex. ["login_wall" => 0.65]
echo $op->outputs['markdown']->url, PHP_EOL;    // URL signée, 15 minutes
print_r($op->structured);

// Les URL signées expirent. Resignez-les plus tard sans refaire le rendu.
$again = $client->v2->getPerceiveOperation($op->operationId);
```

| Option | Type | Description |
|--------|------|-------------|
| `outputs` | `string[]` | Une valeur parmi `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. |
| `extract` | `string[]` | Champs structurés à extraire, par exemple `metadata`, `structured_data`, `headings`, `tables`, `main_content`, `all`. |
| `schema` | `array` | Schéma pour l'extraction structurée assistée par LLM. |
| `onlyMainContent` | `bool` | Supprime l'habillage du site de la sortie Markdown. |
| `waitFor`, `waitTimeoutMs` | `string`, `int` | Attend un sélecteur CSS ou une expression JS après la navigation. |
| `jsCode` | `string` | JavaScript à exécuter sur la page après la navigation. |
| `viewport`, `mobile`, `blockResources` | `array`, `bool`, `string[]` | Géométrie de rendu, et types de ressources à abandonner avant leur chargement. |
| `cacheMode` | `string` | `enabled`, `bypass` ou `refresh`. |
| `headers`, `cookies`, `auth` | `array` | Accès aux pages authentifiées. |
| `respectRobots` | `bool` | Rejette les URL interdites par `robots.txt`. |
| `pdfOptions` | `array` | Mise en page pour la sortie `pdf`. |
| `directDownload` | `bool` | Renvoie les octets bruts de l'artefact. Accepté par `perceive()` uniquement. |
| `proxyUrl`, `geolocation`, `actionChain` | variable | Sérialisés par le SDK, réservés par l'API. |

#### Perception par lot

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

// Les petits lots se terminent en ligne. Les plus gros reviennent en file d'attente, interrogez-les.
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;
```

Jusqu'à 1000 URL partagent un seul bloc d'options. `outputMode` vaut `manifest` (par défaut) ou `zip`. `directDownload` n'est pas accepté ici.

#### Diffusion d'un artefact unique

`perceiveDirect()` court-circuite l'enveloppe JSON et vous rend directement les octets. Exactement une sortie produisant un artefact doit être demandée, et le SDK le vérifie avant l'envoi : `structured` seul lève donc une erreur.

```php
$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;

// Retélécharger un artefact stocké plus tard. Omettez le nom de sortie lorsque
// l'opération n'a produit qu'un seul artefact.
$saved = $client->v2->downloadPerceiveArtifact($direct->operationId, 'pdf');
```

`PerceiveDirectResult` porte `content`, `contentType`, `filename`, `operationId`, `objectKey`, `cacheHit`, `renderQuality`, `sourceStatusCode`, `contentHash` et `warningsCount`. Une `ApiException` avec le statut `410` signifie que l'artefact stocké n'est plus disponible.

### Discover

Énumère les URL d'un site en HTTP uniquement. Aucun rendu navigateur, c'est donc rapide. Voir [la référence discover](/fr/docs/v2-discover).

```php
$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);   // par ex. ["sitemap" => 42, "crawl" => 30]
```

Les options sont `mode`, `maxUrls`, `maxDepth`, `includePatterns`, `excludePatterns`, `sameDomainOnly` et `respectRobots`.

### Lookup

Lance une recherche web catégorisée et, si vous le souhaitez, rend automatiquement les meilleurs résultats. Voir [la référence lookup](/fr/docs/v2-lookup).

```php
$search = $client->v2->lookup('best static site generators', [
    'category' => 'web',        // web | news | images | scholar | patents | maps
    'numResults' => 10,
    'country' => 'us',
    'timeFilter' => 'month',
    'perceiveTop' => 3,         // rend automatiquement les 3 premiers résultats
]);

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

Les options sont `category`, `country`, `locale`, `timeFilter`, `numResults`, `page`, `location`, `autocorrect` et `perceiveTop`.

### Distill

Extraction structurée pilotée par schéma, sur une liste d'URL ou sur un site découvert. Voir [la référence distill](/fr/docs/v2-distill).

```php
$extraction = $client->v2->distill([
    'urls' => ['https://example.com/pricing'],
    'schema' => ['plans' => 'list of plan names with monthly prices'],
    'cssSchema' => [                     // passe CSS gratuite facultative, avant le niveau LLM
        '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;

// Ou découvrez d'abord l'ensemble des URL.
$client->v2->distill([
    'discoverFrom' => ['url' => 'https://example.com', 'mode' => 'sitemap', 'maxPages' => 10],
    'schema' => ['title' => 'page title', 'summary' => 'one-line summary'],
]);
```

`schema` est obligatoire, et exactement l'un de `urls` ou `discoverFrom` doit être présent. Enfreignez l'une de ces deux règles et le SDK lève une `EnconvertException` avant l'envoi. Les autres options sont `waitFor`, `waitTimeoutMs`, `headers`, `cookies` et `respectRobots`.

### Ingest

Transforme un site entier, ou une pile de documents envoyés, en JSONL découpé et prêt pour le RAG, à travers un seul pipeline. Ingest est toujours asynchrone. Voir [la référence ingest](/fr/docs/v2-ingest).

```php
// Depuis un site.
$job = $client->v2->ingest([
    'mode' => 'sitemap',                 // "urls" (par défaut) | "sitemap" | "crawl"
    'url' => 'https://docs.example.com',
    'maxPages' => 100,
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
    'webhookUrl' => 'https://my.app/hooks/enconvert',
]);

// Ou depuis des fichiers envoyés : PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, bureautique hérité et ODF.
$fileJob = $client->v2->ingestFiles(['handbook.pdf', 'notes.docx'], [
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
]);

// Interroger jusqu'à la fin du traitement.
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;  // JSONL signé
}
```

`mode` vaut `urls` par défaut, ce qui exige un tableau `urls` non vide et rejette `url`. Les modes `sitemap` et `crawl` exigent une `url` de départ et rejettent `urls`. Le SDK applique les deux règles localement. Les autres options sont `maxPages`, `maxDepth`, `sameDomainOnly`, `includePatterns`, `excludePatterns`, `respectRobots`, `waitFor`, `waitTimeoutMs`, `chunk` et `webhookUrl`.

```php
// Gestion des jobs.
$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

// Signature des webhooks.
$secret = $client->v2->getWebhookSecret();
echo $secret->signatureHeader, ' ', $secret->signatureScheme, PHP_EOL;
$client->v2->rotateWebhookSecret();               // les anciennes signatures cessent d'être valides
$client->v2->retryIngestWebhook($job->jobId);     // relivrer un callback de fin de traitement
```

### Watch

Refait le rendu d'une URL à intervalle fixe et vous prévient quand elle change. Voir [la référence watch](/fr/docs/v2-watch).

```php
$watcher = $client->v2->createWatcher('https://example.com/pricing', [
    'frequencyMinutes' => 60,            // plancher horaire
    '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' => '']);  // efface le webhook
$client->v2->deleteWatcher($watcher->watcherId);                        // suppression logique, idempotente
```

`createWatcher` accepte `frequencyMinutes`, `diffMode`, `trackFields`, `webhookUrl` et `notifyEmail`. `updateWatcher` accepte ces cinq options plus `status`, et exige au moins un champ, sans quoi elle lève une `EnconvertException`. `listWatchers` accepte `skip` et `limit`.

<div class="alert alert-warning">
<strong>Les diffs de snapshots contiennent du contenu de page non fiable.</strong> Les entrées `changes` d'un `WatcherSnapshot` sont copiées depuis la page surveillée. Échappez-les avec `htmlspecialchars()` avant de les afficher dans votre propre interface.
</div>

---

## Options PDF

Passées via le tableau `pdfOptions` de `convertUrlToPdf`, `convertDocument`, `convertToPdf` (niveaux de gris uniquement), `convertWebsiteToPdf`, et de `perceive` en V2 avec une sortie `pdf`. Le SDK sérialise les clés camelCase vers le format attendu par l'API, et n'envoie que les clés que vous définissez.

```php
$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',
]);
```

| Champ | Type | Description |
|-------|------|-------------|
| `pageSize` | `string` | De `A0` à `A6`, de `B0` à `B5`, `Letter`, `Legal`, `Tabloid`, `Ledger`. Ignoré lorsque `pageWidth` et `pageHeight` sont tous deux définis. |
| `pageWidth`, `pageHeight` | `float` | Taille de page personnalisée, en millimètres. À définir ensemble. |
| `orientation` | `string` | `portrait` ou `landscape`. |
| `margins` | `array` | `['top' => ..., 'bottom' => ..., 'left' => ..., 'right' => ...]` en millimètres. |
| `scale` | `float` | Échelle de rendu, de `0.1` à `2.0`. Sortie paginée uniquement. |
| `grayscale` | `bool` | Post-traite le PDF en niveaux de gris. |
| `header` | `array` | `['content' => '<html>', 'height' => 15]`. Hauteur en millimètres. |
| `footer` | `array` | Même forme que `header`. |

Le contenu des en-têtes et pieds de page prend en charge les variables de gabarit `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}` et `{{url}}`. La référence complète des paramètres se trouve dans [paramètres et options](/fr/docs/parameters-options).

---

## Gestion des erreurs

Tout ce que lève le SDK descend de `Enconvert\Exception\EnconvertException` : un seul bloc catch peut donc borner toute la surface.

```php
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());
}
```

| Classe | Déclenchée sur | Code de statut |
|-------|-----------|-------------|
| `AuthenticationException` | Clé API invalide, manquante ou révoquée | réponses `401` et `403` |
| `QuotaException` | HTTP 402 | `402` |
| `RateLimitException` | Trop de requêtes | `429` |
| `ApiException` | Toute autre réponse 4xx ou 5xx | le code réel |
| `EnconvertException` | Classe de base, et tout échec côté client | aucun |

`ApiException::getStatusCode()` renvoie le statut numérique ; le message est préfixé par `[code]`. Une réponse `403` est mappée sur `AuthenticationException`, dont le `getStatusCode()` indique `401` : branchez donc sur la classe plutôt que sur le nombre lorsque vous devez distinguer les deux.

`EnconvertException` est également levée sans aucun aller-retour réseau lorsque le SDK peut établir que la requête est vouée à l'échec : clé API vide, chemin de fichier manquant, extension de fichier non reconnue, paire de conversion non implémentée, appel `distill` sans schéma ou avec à la fois `urls` et `discoverFrom`, incohérence entre le mode `ingest` et la charge utile, liste `ingestFiles` vide, appel `perceiveDirect` qui ne nomme pas exactement une sortie d'artefact, appel `updateWatcher` sans aucun champ, et tout échec de transport Guzzle (remonté sous la forme `HTTP request failed: ...`).

La table complète des messages se trouve dans la [référence des codes d'erreur](/fr/docs/error-codes).

---

## Récupération des timeouts

Les conversions de documents volumineux et les pages lentes peuvent dépasser un timeout de reverse proxy même quand la conversion finit par réussir sur le serveur. Le SDK s'en remet tout seul :

1. Avant chaque requête de conversion, il génère un identifiant de job hexadécimal de 32 caractères et l'envoie comme `job_id` dans le corps ou dans le formulaire multipart.
2. Si cette requête revient en 5xx, le SDK cesse de lever une erreur et se met à interroger `GET /v1/convert/status/{job_id}` toutes les 3 secondes.
3. Dès que le job affiche `success`, le SDK renvoie le résultat. S'il affiche `failed`, il lève une `ApiException` portant le message d'erreur du serveur.
4. Un `404` pendant l'interrogation signifie « pas encore enregistré » et l'interrogation continue. Le délai maximal est de 5 minutes, au-delà duquel le SDK lève `ApiException(504, 'Conversion timed out')`.

Vous n'avez aucun code à écrire pour cela. Deux exceptions volontaires : les soumissions de lots de site entier (`convertWebsiteToPdf`, `convertWebsiteToScreenshot`) n'ont pas de ligne de job, si bien qu'un 5xx y remonte immédiatement, et les méthodes V2 n'utilisent pas du tout le polling de job.

Les réponses réussies qui omettent `job_id` reçoivent l'identifiant généré par le client, si bien que `$result->jobId` reste toujours utilisable avec `getJobStatus()`.

---

## Configuration

```php
use Enconvert\Client;

$client = new Client(getenv('ENCONVERT_API_KEY'), [
    'timeout' => 300,                            // secondes
    'base_url' => 'https://api.enconvert.com',   // à remplacer pour une passerelle auto-hébergée
]);
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `$apiKey` (premier argument) | `string` | obligatoire | Clé API privée (`sk_live_...`). Une chaîne vide lève une `EnconvertException`. |
| `timeout` | `int\|float` | `300` | Timeout de requête en **secondes**, l'unité idiomatique de Guzzle. S'applique à chaque requête HTTP émise par le client. Le délai d'interrogation des jobs est distinct et fixé à 300 secondes. |
| `base_url` | `string` | `https://api.enconvert.com` | Hôte de l'API. Les barres obliques finales sont supprimées. |

Notez que la clé d'option est en snake_case (`base_url`) alors que toutes les autres options de requête du SDK sont en camelCase. Les requêtes sont authentifiées par un en-tête `X-API-Key` ; les téléchargements déclenchés par `saveTo` vont directement au stockage et n'envoient volontairement aucune clé, puisque l'URL est déjà signée.

<div class="alert alert-warning">
<strong>Ne codez jamais la clé API en dur.</strong> Lisez-la depuis une variable d'environnement ou votre gestionnaire de secrets, et gardez-la hors du contrôle de version et hors de tout ce qui part vers un navigateur. Générez et faites tourner vos clés depuis le <a href="/fr/dashboard">tableau de bord</a>, et consultez le <a href="/fr/docs/authentication">guide d'authentification</a> pour les types de clés.
</div>

---

## Structure du résultat

Chaque conversion de fichier unique renvoie un `Enconvert\Model\ConversionResult` doté de propriétés publiques en lecture seule :

```php
final class ConversionResult
{
    public readonly string $presignedUrl;              // URL de téléchargement signée
    public readonly string $objectKey;                 // clé de l'objet de stockage
    public readonly string $filename;                  // nom de fichier côté serveur
    public readonly int|float|null $fileSize;          // octets
    public readonly int|float|null $conversionTimeSeconds;
    public readonly ?string $jobId;                    // l'id utilisé pour la récupération des timeouts
}
```

Les URL présignées expirent au bout de 15 minutes et peuvent être utilisées plusieurs fois avant cela. Pour un accès permanent, téléchargez les octets (passez `saveTo`, ou récupérez l'URL vous-même) et stockez-les dans votre propre bucket.

Les autres types de résultats suivent le même schéma : `JobStatus`, `BatchSubmission`, `BatchStatus` avec ses `BatchItem[]`, et, sous `Enconvert\Model\V2`, `PerceiveResult`, `PerceiveDirectResult`, `PerceiveBatchResult`, `OutputArtifact`, `DiscoverResult`, `LookupResult`, `LookupItem`, `DistillResult`, `DistillItem`, `IngestJob`, `IngestJobList`, `IngestJobSummary`, `Watcher`, `WatcherList`, `WatcherSummary`, `WatcherSnapshot`, `WatcherSnapshotList`, `WebhookSecret`, `WebhookRetryResult` et `Tokens`. Les champs arrivent en snake_case sur le réseau et sont mappés vers des propriétés camelCase ; les charges utiles fournies par l'utilisateur, comme les schémas, les données extraites, les champs suivis et les entrées de diff, passent sans modification.

---

## Source et problèmes

- **Packagist :** [enconvert/enconvert-php](https://packagist.org/packages/enconvert/enconvert-php)
- **GitHub :** [conversionapi/php-sdk](https://github.com/conversionapi/php-sdk) · [ouvrir un ticket](https://github.com/conversionapi/php-sdk/issues)
- **Licence :** MIT
- **Autres clients :** [tous les SDK](/fr/docs/sdks) · [endpoints REST](/fr/docs/endpoints-overview)

---

## Questions fréquentes

### Comment convertir des fichiers en PHP avec Composer ?

Exécutez `composer require enconvert/enconvert-php`, construisez `new Enconvert\Client($apiKey)`, et appelez une méthode comme `convertUrlToPdf`, `convertImage`, `convertDocument`, `convertToPdf` ou `convertToMarkdown`. Ajoutez `saveTo` à n'importe laquelle d'entre elles et le SDK écrit pour vous les octets convertis vers ce chemin local, en créant les répertoires parents au passage.

### Comment convertir un DOCX en PDF en PHP ?

Appelez `$client->convertDocument('report.docx', ['saveTo' => 'report.pdf'])`. Le format de sortie vaut `pdf` par défaut, aucun `outputFormat` n'est donc nécessaire. Le format d'entrée est lu depuis l'extension du fichier, et le SDK vérifie la paire `doc-to-pdf` contre sa table des 43 conversions implémentées avant d'envoyer quoi que ce soit : une paire non prise en charge échoue donc instantanément, avec un message listant ce qui est valide pour cette entrée.

### Comment convertir une URL en PDF en PHP ?

Appelez `$client->convertUrlToPdf('https://example.com', ['saveTo' => 'page.pdf'])`. Par défaut, la page est rendue en une seule page continue, dans une fenêtre d'affichage de 1920x1080, avec le chargement des médias et le défilement activés. Passez `singlePage` à `false` et fournissez `pdfOptions` lorsque vous voulez une vraie pagination avec une taille de page, des marges, des en-têtes et des pieds de page.

### Comment récupérer une page web en PHP et obtenir du Markdown propre ?

Utilisez l'espace de noms V2 : `$client->v2->perceive($url, ['outputs' => ['markdown', 'structured']])`. Il rend la page dans un vrai navigateur, les sites très chargés en JavaScript fonctionnent donc, et il renvoie une URL Markdown signée plus des données structurées en ligne. Vérifiez `renderQuality` sur le résultat avant de faire confiance au contenu : un score bas avec des `deductions` renseignées signifie que le rendu est tombé sur une page anti-bot, un mur de connexion ou une coquille vide. Pour un chemin plus léger et ponctuel, sans les fonctionnalités V2, `convertUrlToMarkdown` fonctionne aussi.

### Comment convertir du HEIC en WebP en PHP ?

Appelez `$client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp'])`. Toute paire parmi `jpeg`, `png`, `svg`, `heic` et `webp` est prise en charge, ainsi que la rastérisation `pdf` vers `jpeg`. Vous pouvez aussi passer des octets bruts sous la forme `['data' => $bytes, 'filename' => 'photo.heic']`, où c'est le nom de fichier qui détermine le format d'entrée et le type MIME.

### Le SDK PHP EnConvert fonctionne-t-il avec Laravel ou Symfony ?

Oui. Le package est une simple bibliothèque PSR-4 avec Guzzle 7 pour seule dépendance et aucun couplage à un framework : il s'intègre donc tel quel dans Laravel, Symfony, WordPress ou un script nu. Enregistrez `Enconvert\Client` dans votre conteneur avec la clé issue de votre configuration d'environnement, et injectez-le partout où vous en avez besoin.

### Que se passe-t-il quand une conversion dure plus longtemps que le timeout HTTP ?

Le SDK s'en remet tout seul. Il envoie un `job_id` généré côté client avec chaque requête de conversion, et si la requête renvoie un 5xx il interroge silencieusement `GET /v1/convert/status/{job_id}` toutes les 3 secondes pendant 5 minutes au maximum, en renvoyant le résultat dès que le job enregistre `success`. Passé ce délai, il lève `ApiException(504, 'Conversion timed out')`. Les soumissions de lots de site entier ne sont pas concernées, puisqu'elles n'ont pas de ligne de job.

### Combien de temps l'URL de téléchargement présignée est-elle valide ?

Les URL de téléchargement présignées expirent au bout de 15 minutes et peuvent être utilisées plusieurs fois avant cela. Les URL d'artefacts V2 portent un `expiresIn` de 900 secondes pour la même raison, et `getPerceiveOperation($operationId)` les resigne à partir des clés d'objets stockées, sans refaire le rendu de la page. Pour quoi que ce soit de permanent, téléchargez les octets et conservez-les dans votre propre stockage.

### Quelle version de PHP le SDK exige-t-il ?

PHP 8.1 ou plus récent. Le SDK utilise partout des propriétés en lecture seule, des types union façon énumération, des arguments nommés et des expressions `match` : les versions 8.0 et antérieures ne sont donc pas prises en charge. Sa seule dépendance à l'exécution est `guzzlehttp/guzzle ^7.8`.
