Clipboard and drag data
MyPHP\Transfer describes serialized content shared by the system
clipboard and native drag and drop. A Data value
contains ordered items. Each Item offers alternative formats
(Representations) for the same content; separate items describe
separate values. Custom formats use MIME names and explicit encodings.
Native data never contains a PHP value of the process.
use MyPHP\Clipboard;
use MyPHP\ClipboardOptions;
use MyPHP\Transfer\{Data, Format, Item, Representation};
$noteFormat = 'application/vnd.example.note+json';
$frozen = ['title' => 'Groceries', 'body' => 'Milk']; // arrays are copies: frozen as made
$data = new Data(new Item(
Representation::bytes(Format::TEXT, $frozen['title']),
Representation::bytes(Format::HTML, '<b>' . htmlspecialchars($frozen['title']) . '</b>'),
Representation::lazy($noteFormat, fn() => json_encode($frozen, JSON_THROW_ON_ERROR)),
));
Clipboard::write($data, new ClipboardOptions(
onRelease: function () { /* release resources captured by these providers */ },
));
// In a view: $c->dragData($row, $data) — the same model, with an independent provider cache.
Empty bytes are valid. Format::TEXT, HTML, PNG, URI_LIST,
FILE_LIST and MARKDOWN name the common representations. Data::text(),
Data::url() and Data::files() construct common offers. Files are
absolute paths encoded as escaped file URLs; paths() accepts local paths
only, ignoring remote authorities and non-file URLs. Files are neither read
nor deleted.
Clipboard discovery and reading
Clipboard::formats() returns portable names without requesting
representations. Data::preferredFormat() and Offer::preferred()
negotiate in receiver preference order. availableFormats() keeps the
platform's conventions: native UTIs on macOS, GTK targets on Linux.
Request only formats the receiver can use:
$formats = Clipboard::formats();
if (($format = Data::preferredFormat($formats, $noteFormat, Format::HTML, Format::TEXT)) !== null) {
$bytes = Clipboard::readFormat($format);
// Decode the explicitly agreed encoding.
}
// Read accepted alternatives together; other providers are not requested.
$data = Clipboard::read($noteFormat, Format::HTML, Format::TEXT);
if (($format = $data->preferred($noteFormat, Format::HTML, Format::TEXT)) !== null) {
$bytes = $data->read($format);
}
read() without arguments copies all advertised representations. An empty
clipboard returns empty data. An explicit request with no matching format
throws FormatUnavailable. readFormat() coalesces items as
Data::read() does: text joins with newlines, URI and file lists with
CRLF, and other formats use the first matching item.
Reads return materialized data with no native providers. It remains usable
after the owner changes, a window closes, or the source application exits,
and can be offered in a later drag. Reads that observe ownership changing
throw ClipboardChanged. Clipboard access from a clipboard provider
throws ClipboardReentrant, preventing recursive reads and replacement.
readText(), writeText(), readHTML(), writeHTML(), readImage(),
writeImage() and clear() keep their simple forms. writeHTML() offers
HTML and a text fallback. writeImage() validates the PNG before replacing
existing data. writeFiles() and readFiles() exchange native file lists.
Lazy providers and ownership
Each write and each drag takes a new Data::snapshot(). A lazy provider
runs at most once for that snapshot when requested; success and errors are
cached. Discovery and preference negotiation request no bytes. Native
receivers, including clipboard managers, may request bytes immediately
after publication. read() requests only its selected formats. A provider
error rejects that read; other representations remain available.
Providers execute on the main thread and must return promptly. Prepare
costly content beforehand, and freeze the value shared by alternative
representations. Never read the same provider recursively, or access the
clipboard from a clipboard provider.
Clipboard ownership belongs to the application, independent of any
source window. Closing a window leaves its copied content and providers
available. Keep provider resources alive until
ClipboardOptions::$onRelease, which runs once on the main thread.
Replacement, clear(), a successful flush(), and app shutdown release
providers. Drag sources instead have their DragOptions::$done callback.
Clipboard::flush() resolves every owned representation before handing it
to native storage, so that it outlives the app. Quitting flushes
automatically on a best-effort basis and then releases remaining provider
resources. On Linux a clipboard manager is required for persistence after
exit.
Native interoperability
| Platform | Clipboard | Representations |
|---|---|---|
| macOS | NSPasteboard with pasteboard item providers | Text, HTML, PNG and file URLs use system UTIs; MIME names map through UTType, with a reversible identifier fallback. Item boundaries are preserved. |
| Linux | GTK 3 clipboard selections | MIME targets, UTF8_STRING for text, text/uri-list for URLs and files. |
The clipboard example and the [drag-drop
example](../examples/drag-drop) demonstrate these operations.