---
title: "Architecture"
description: "Architecture"
type: "docs"
category: "doc"
tags: []
authors: [Anonymous]
date: "2026-09-09"
last_update: "2026-09-09"
time_minutes: 3
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/utils/escpos/architecture"
---

# Architecture

`derafu/escpos` wraps [mike42/escpos-php](https://github.com/mike42/escpos-php) instead of implementing the ESC/POS protocol itself — but that library is treated as a replaceable implementation detail, not part of this package's contract. Nothing in the public API accepts or returns a `Mike42\Escpos\*` type or constant; see [Vocabulary](./vocabulary) and [Printing Images](./images) for the two places that leak would normally happen (command values, and image types) and how they are closed.

## Why this matters

If a consumer's code imports `Mike42\Escpos\*` directly to call this package, upgrading `mike42/escpos-php` to a new major version can break that consumer's code too — even though they never touched the underlying library's version themselves. Isolating it behind this package's own API means a breaking change in the underlying library only needs to be absorbed in one place: here.

## The seam: `PrinterInterface`

`Derafu\Escpos\Contract\PrinterInterface` defines every operation `EscposPrinter` needs from a backend: `text()`, `feed()`, `selectPrintMode()`, `setJustification()`, `barcode()`, `pdf417()`, `image()`, `cut()`, `pulse()`, `getOutput()`, `close()`.

`Derafu\Escpos\Adapter\Mike42Printer` is the only class in this package allowed to `use Mike42\Escpos\*`, and it is the default implementation of `PrinterInterface`. `EscposPrinter` is a thin facade: it owns option resolution, the `sprintf()`/line-break convenience of `print()`/`println()`, special character handling, and the `end()` workflow (finish, optionally cut and pulse) — and delegates everything else to whichever `PrinterInterface` it was given.

```php
use Derafu\Escpos\Contract\PrinterInterface;
use Derafu\Escpos\EscposPrinter;

// Normally you never need this — omitting the second argument builds the
// default Mike42Printer from your options array.
$printer = new EscposPrinter($options, $customPrinterImplementation);
```

That second constructor argument is the extension point: a different backend (or a test double) can be swapped in without changing `EscposPrinter`'s public API, or any code that already depends on `PrinterInterface` instead of the concrete class.

## Exceptions

Every exception this package throws implements `Derafu\Escpos\Exception\EscposExceptionInterface`, so you can catch anything it raises without needing to know the specific class:

| Exception | Thrown when |
|---|---|
| `ConnectorNotSupportedException` | The configured connector type has no known implementation (only `memory` exists today). |
| `ProfileNotFoundException` | The configured capability profile does not exist. |
| `UnbufferedConnectorException` | `dump()`/`end()` is called on a connector that does not buffer data in memory (`memory` always does; this exists so the failure mode is a clear exception if that ever changes, not a corrupted read). |
| `InvalidImageDataException` | `RasterImage::fromPngData()` is given data that cannot be decoded as an image. |
| `NetworkDeliveryException` | `NetworkTransport::send()` cannot connect, or cannot deliver all bytes. |

None of these extend a `Mike42\Escpos\*` exception, for the same reason described above.



---
Last updated on 09/09/2026

