AI Image Generator — PHP source
Turn a text prompt into a 1024×1024 image with FLUX.1 [schnell] on Cloudflare Workers AI. No account, no API key — rate-limited for fair use. Your prompt goes to the model through our Worker; we store no prompts and no images.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* AI Image Generator — prompt validation, usage-gate verdicts, and ?prompt=
* URL state for FLUX.1-schnell image generation.
*
* Language: PHP (8.2, standard library only; mbstring for char counts)
* Source: CosmoDev polyglot showcase port, from src/lib/image-gen.ts.
*/
const MODEL = '@cf/black-forest-labs/flux-1-schnell';
const STEPS = 4; // flux-1-schnell's design point -> 57.6 neurons/image
const PROMPT_MAX_CHARS = 600; // UX clamp, below the model's 2048 limit
const IP_HOUR_CAP = 10;
const DAILY_CAP = 100;
/**
* Normalize CRLF/CR to "\n", trim, enforce 1..600 chars, and reject control
* chars except "\n" (C0 minus U+000A, DEL, C1). Returns the prompt, or null
* with $reason set to 'empty' | 'tooLong' | 'controlChars'.
*/
function validate_prompt(string $raw, ?string &$reason = null): ?string
{
$prompt = trim(preg_replace('/\r\n?/', "\n", $raw));
if ($prompt === '') {
$reason = 'empty';
return null;
}
if (mb_strlen($prompt) > PROMPT_MAX_CHARS) {
$reason = 'tooLong';
return null;
}
if (preg_match('/[\x00-\x09\x0B-\x1F\x7F-\x9F]/u', $prompt) === 1) {
$reason = 'controlChars';
return null;
}
return $prompt;
}
/** Minutes (>= 1) until the next UTC midnight — the daily window reset. */
function minutes_to_utc_midnight(DateTimeImmutable $now): int
{
$utc = $now->setTimezone(new DateTimeZone('UTC'));
$midnight = $utc->modify('tomorrow'); // 00:00 the next UTC day
return max(1, (int) ceil(($midnight->getTimestamp() - $utc->getTimestamp()) / 60));
}
/**
* Daily cap wins: the site-global budget resets at UTC midnight. Returns
* ['ok' => true] or ['ok' => false, 'error' => ..., 'retryAfterMinutes' => ...].
*/
function check_gate(int $ip_count, int $daily_count, ?DateTimeImmutable $now = null): array
{
if ($daily_count >= DAILY_CAP) {
return ['ok' => false, 'error' => 'RATE_LIMITED_DAILY',
'retryAfterMinutes' => minutes_to_utc_midnight($now ?? new DateTimeImmutable('now'))];
}
if ($ip_count >= IP_HOUR_CAP) {
return ['ok' => false, 'error' => 'RATE_LIMITED_IP', 'retryAfterMinutes' => 60];
}
return ['ok' => true];
}
/** Encode the shareable state: '' when there is nothing to share. */
function encode_url_state(string $prompt): string
{
return $prompt === '' ? '' : '?prompt=' . rawurlencode($prompt);
}
/** Inverse of encode_url_state — a missing param decodes to ''. */
function decode_url_state(string $search): string
{
foreach (explode('&', ltrim($search, '?')) as $pair) {
if (str_starts_with($pair, 'prompt=')) {
return rawurldecode(substr($pair, 7));
}
}
return '';
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →