Framework
Features

Providers

Ready-made integrations with the outside world — AI models, sign-in, maps, payments, caching and PDFs. Each is a small static class configured from the environment, so an app turns one on by filling in its keys.

The providers

ProviderDoes
OpenAI / OllamaText completions with a schema, and audio transcription.
Google / Microsoft / FacebookResolve the account behind a sign-in token.
Google MapsGeocoding, distances and static map images.
MercadoPagoCheckout links, payment data, refunds.
RedisCache entities between requests.
PDFRender HTML into a PDF.
MailChimpAudiences, subscribers and campaigns.
Curl / MustacheThe HTTP client and template renderer the rest are built on.

The email transports — SMTP, SendGrid, Mailgun, Mailjet and Mandrill — are providers too, covered where they are used in Emails, and OneSignal in Notifications.

OpenAI

OpenAI talks to the OpenAI API with OPEN_AI_KEY. A completion takes a model and a prompt, and optionally a Dictionary describing the JSON schema you want back — which is how a response comes out structured rather than as prose:

use Framework\Provider\OpenAI;

$result = OpenAI::createCompletion(
    model:  "gpt-4o-mini",
    prompt: "Summarize this ticket in one line: $text",
);

$result = OpenAI::createCompletion(
    model:  "gpt-4o-mini",
    prompt: $prompt,
    schema: $schema,     // ask for a structured answer
);

Audio is transcribed the same way, and the models available can be listed for a picker:

OpenAI::transcribeAudio($fileContent, $fileName, $language);
OpenAI::getModelSelect();      // a Select list of the models
OpenAI::modelExists($model);

For retrieval it manages files and vector stores — uploadFile, getFile, deleteFile, and the …VectorStore / …VectorFile family, with waitForVectorFile() to block until one finishes indexing. setTempApiKey() runs a call with a different key, for a customer's own account.

Ollama

Ollama is the same completion API against a local Ollama server, pointed at by OLLAMA_URL. The signature matches OpenAI's, so the two swap without touching the caller:

use Framework\Provider\Ollama;

$result = Ollama::createCompletion(
    model:  "llama3",
    prompt: $prompt,
    schema: $schema,
);

Sign in with Google, Microsoft or Facebook

Google, Microsoft and Facebook each resolve the account behind a token the client obtained, so a social sign-in ends with an email you can match against a credential:

use Framework\Provider\Google;

$email   = Google::getAuthEmail($accessToken);
$account = Google::getAuthAccount($accessToken);   // the full account

All three expose the same two methods, so the sign-in route only differs in which class it calls. Google uses GOOGLE_CLIENT / GOOGLE_SECRET and Microsoft MICROSOFT_CLIENT.

Google Maps

GoogleMap needs GOOGLE_MAP_ACTIVE and GOOGLE_MAP_API_KEY. It turns an address into coordinates and back, measures distances, and builds a static map image url:

use Framework\Provider\GoogleMap;

GoogleMap::isActive();

$location = GoogleMap::getFromAddress("Av. Corrientes 1234, Buenos Aires");
$address  = GoogleMap::getFromLatLng($latitude, $longitude);
$address  = GoogleMap::getAddress($placeID);

$km  = GoogleMap::calculateDistance($lat1, $lng1, $lat2, $lng2);
$url = GoogleMap::getImageUrl($latitude, $longitude, zoom: 16, width: 400, height: 400);

MercadoPago

MercadoPago covers a marketplace checkout. It builds the payment url the buyer is sent to, reads the payment back when the webhook arrives, and cancels or refunds it:

use Framework\Provider\MercadoPago;

$payment = MercadoPago::createPaymentUrl(
    reference:      $orderID,
    items:          $items,
    payer:          $payer,
    marketplaceFee: $fee,
);

$data = MercadoPago::getPaymentData($paymentID);
MercadoPago::refundPayment($paymentID);
MercadoPago::cancelPayment($paymentID);

Sellers connect their own accounts through OAuth — getAuthUrl(), then createAccessToken() and recreateAccessToken() to refresh it. The MP_* keys hold the client id and secret, the webhook signature, and the return and notification paths.

Redis

Redis caches entities between requests when REDIS_ACTIVE is on. It is keyed by a module name and an id, and hands back a Dictionary:

use Framework\Provider\Redis;

Redis::set("product", $productID, $data);
$data = Redis::get("product", $productID);

if ($data->isEmpty()) {
    // not cached — read it and store it
}

With Redis off both calls become no-ops, so the caching can be left in the code either way.

PDF

PDF renders HTML into a PDF through Dompdf, an optional dependency. Build the markup however you like — a Mustache template is the usual way — and hand it over:

composer require dompdf/dompdf
use Framework\Provider\PDF;

$pdf = PDF::create(
    html:            $html,
    title:           "Invoice $number",
    withPageNumbers: true,
);

getImageData() and getCroppedImageData() inline an image as a data url, which is how a logo or a chart travels inside the document.

MailChimp

MailChimp manages an audience and its campaigns, separate from the transactional email sending. It is gated by the MAILCHIMP_* keys, each part switched on its own — subscribers, creating contacts, sending campaigns:

use Framework\Provider\MailChimp;

MailChimp::addSubscriber($email, $data);
MailChimp::editSubscriber($email, $data);
MailChimp::deleteSubscriber($email);
MailChimp::getSubscriberStatus($email);

MailChimp::sendCampaign($campaignID);
MailChimp::getReport($campaignID);       // opens, clicks, sends

addSubscriberBatch() imports many at once, and the get…Details family reads back who opened, clicked or received a campaign.

Curl

Curl is the HTTP client every provider on this page is built on, and the one to use for an API the framework does not wrap. It has enough options to deserve its own page — see Curl.

use Framework\Provider\Curl;
use Framework\Provider\Type\CurlMethod;

$result = Curl::execute(CurlMethod::GET, $url, $params);

Mustache

Mustache renders Mustache templates, through the optional mustache/mustache package. It is what the build renders its .mu files with, what wraps an email in its HTML template, and what fills the {{placeholders}} of stored content:

use Framework\Provider\Mustache;

$html = Mustache::render($template, [
    "name"  => $name,
    "items" => $items,
]);

getError() returns why a render failed, when it did.