Flow PHP Extension
Flow stores durable datasets (to_floe()) and caches intermediate ones (DataFrame::cache(),
sort / join / group-by spill buckets) using the native Floe binary format (.floe) - the schema
is written once per file and rows carry raw values only, which makes both the payload and the
hydration dramatically cheaper than native PHP serialize()/unserialize().
This extension encodes and decodes Floe frames natively in Rust via
ext-php-rs. The pure-PHP implementation in Flow\Floe
(flow-php/etl) is the canonical behavior reference and works without the extension - loading it is
purely an optimization. File header and footer assembly always stay in PHP.
You never need to call this extension directly.
Flow\Floe\FloeReader/FloeWriter- used byfrom_floe()/to_floe(), the cache and the sort / join / group-by buckets caches - route whole batches to it automatically whenextension_loaded('flow_php')is true.
Loading the Extension
In php.ini
extension = flow_php
During Development
php -d extension=./ext/modules/flow_php.so your_script.php
Usage
The extension is used implicitly through the ETL cache:
<?php
use function Flow\ETL\DSL\{df, from_array, to_stream};
df()
->read(from_array($bigDataset))
->cache('my-dataset') // serialized with the extension when loaded
->write(to_stream(__DIR__ . '/output.csv'))
->run();
The extension registers two native classes; the PHP side (FloeStreamWriter/FloeStreamReader) owns
file framing, sectioning, partitions, footer and - on read - skip/limit/padding, and picks the native
implementation automatically:
Flow\Floe\RustFloeEncoderNative- the Floe ROW frame-body codec:encode(list<TypedRowValues>, schemaBody)returns the encoded frame bodies,decode(list<string>, schemaBody)returnslist<Flow\ETL\Row\RawRowValues>. The userland wrapperFlow\Floe\NativeFloeEncodercarries theFlow\ETL\Row\Encoderinterface, andFlow\Floe\AdaptiveFloeEncoder- built by every writer/reader - selects it overFlow\Floe\PhpFloeEncoderwhen the extension is loaded.Flow\ETL\Row\RustRowHydratorNative- the nativehydrate/cast/dehydratebehindFlow\ETL\Row\NativeRowHydrator, whichFlow\ETL\Row\AdaptiveRowHydrator(the config default) selects when the extension is loaded - used by adapter loaders and raw-scalar extractors such as CSV, JSON or XML.cast(batch, Schema)casts raw scalars and builds entries in a single native pass; values outside the proven native subset (and every exotic type such as enum, xml or time) cast per value through the schema's PHPType::cast, so results and exceptions matchPhpRowHydratorexactly. Schema-lesscast(type inference) stays PHP. Hydrate and cast plans are cached on theSchemaobject identity and rebuilt only when a different schema arrives.
All extension failures throw Flow\Floe\Exception\ExtensionException; FloeReader/FloeWriter wrap
it as Flow\Floe\Exception\FloeException. There is no silent fallback to the PHP engine.
Found a typo or an outdated section? Edit this page on GitHub