Guides
Backends
phibble separates what a pipeline does from where it runs. Every verb is part of the Phibble\Contracts\Table interface, and three classes implement it:
| Backend | Class | Runs | Use it for |
|---|---|---|---|
| In memory | Phibble\DataFrame |
PHP arrays | Small and medium data, closures, anything |
| SQL | Phibble\Sql\SqlTable |
SQLite, PostgreSQL, or MySQL | Data that lives in a database |
| Polars | Phibble\Polars\PolarsTable |
Polars, through the php_polars extension | Large data in memory |
All three classes are part of phibble. PolarsTable uses the php_polars extension, which phpolars provides.
Code written against Table, including phplyr’s verbs, runs on any of them. A backend that cannot run a step exactly, such as a PHP closure or a function with no exact translation, hands that step to DataFrame and continues. The values then match DataFrame with these exceptions:
- A database cannot represent some of R’s values, such as
NaNin MySQL. The SQL tables page lists these cases. - Rows from a SQL join, and from a table without
arrange(), come back in the order the database returns them. - Floating-point results can differ in the last digits. The SQL and Polars tests compare floats rounded to nine decimal places.
The Table contract page states the rules each backend follows.
Choosing and switching
use Phibble\DataFrame;
use function Phibble\{tbl, polars};
use function Phibble\Expr\{col, mean, n};
$df = DataFrame::fromCsv('sales.csv');
$sql = tbl($pdo, 'sales');
$fast = polars($df);
$summary = fn ($t) => $t
->filter(col('units')->gt(0))
->groupBy(['store'])
->summarize(['n' => n(), 'avg' => mean(col('price'))]);
$summary($df);
$summary($sql)->collect();
$summary($fast)->collect();$df holds the data in memory, $sql reads it from the database, and $fast holds it in Polars. On $sql, the summary runs as one SQL query. All three classes have summarize() as an alias of the Table method summarise(). Code that accepts any Table implementation should call summarise(), since the interface declares only that name.
collect() turns any table into a DataFrame. PolarsTable::fromTable() and polars() move a table into Polars; tbl() opens a database table or query.
The Polars backend
phibble’s Phibble\Polars\PolarsTable needs the php_polars extension, which phpolars builds. phibble does not require it: composer.json lists it as a suggestion, and the class is only loaded when used. Expressions, joins (including join_by comparisons and rolling joins), pivots, and the lubridate and stringr functions run natively in Polars. Steps it cannot run, such as PHP closures and str_to_title(), run in DataFrame. PolarsTable::$fallbacks counts those steps. Set PolarsTable::$strict = true to throw PolarsFallback instead. The message names the verb and the reason.
Two kinds of list-column stay in DataFrame. Polars stores named arrays as structs, which have every key in every row, so fromTable() throws when a list-column holds named arrays with different keys. A list-column of DataFrames cannot be stored in Polars either.
The Polars test suite runs phibble’s dplyr, tidyr, lubridate, and stringr reference cases on PolarsTable and skips when the extension is not loaded.
From phpstats/phibble@36d0f3f.