The framework exposes a set of global helper functions available everywhere in the application — controllers, middleware, views, console commands, and event listeners. They are loaded via functions/functions.php early in the boot sequence, before the framework kernel is instantiated.
View-specific helpers (pageTitle(), getErrors(), getMessages(), formValue(), input(), checkbox(), asset(), numFormat(), routeLink(), _t(), _tt()) are documented in Views. This page covers everything else.
em( string $connectionName ): Doctrine\ORM\EntityManager
Returns the Doctrine EntityManager for the given connection. The connection name maps to entity manager definitions in config/packages/doctrine.yaml. A connection name is always required:
// Named connection (always explicit)
$user = em( 'connection_name' )->getRepository( User::class )->find( $id );
$analyticsEm = em( 'analytics' );
This retrieves the EntityManager from Symfony's DI container — it's the same instance Symfony services use, not a separate connection.
qb( string $connectionName ): Doctrine\ORM\QueryBuilder
Returns a fresh QueryBuilder instance from the specified EntityManager. Shorthand for em( $connectionName )->createQueryBuilder():
$players = qb( 'connection_name' )
->select( 'u' )
->from( User::class, 'u' )
->where( 'u.userGroup = :group' )
->setParameter( 'group', UserGroupEnum::USER->getId() )
->orderBy( 'u.createdAt', 'DESC' )
->setMaxResults( 10 )
->getQuery()
->getResult();
ca( string|null $cacheAdapter = null ): AbstractCacheAdapter
Returns the best available cache adapter. When called with no argument, returns APCuCacheAdapter if APCu is available and enabled, RedisCacheAdapter otherwise. Pass an explicit adapter class constant to force a specific adapter:
// Auto-select (APCu if available, Redis otherwise)
ca()->set( 'my_key', $value, 3600 );
// Force Redis
ca( AbstractCacheAdapter::REDIS_CACHE_ADAPTER )->set( 'my_key', $value );
// Force APCu
ca( AbstractCacheAdapter::APCU_CACHE_ADAPTER )->set( 'my_key', $value );
// Force Memcached
ca( AbstractCacheAdapter::MEMCACHED_CACHE_ADAPTER )->set( 'my_key', $value );
Use ca() for general application cache. Use rca(), aca(), or mca() directly only when you specifically need features of a particular adapter.
rca(): RedisCacheAdapter
Returns the Redis cache adapter singleton. Use when you specifically need Redis — pattern deletion, pub/sub, or guaranteeing data lands in Redis regardless of APCu availability:
rca()->set( 'player:123:score', 4500, 3600 );
rca()->get( 'player:123:score' );
rca()->has( 'player:123:score' );
rca()->delete( 'player:123:score' );
rca()->deleteByKeyPattern( 'player:123:*' );
rca()->clear();
// Pub/Sub
rca()->pub( 'game:events', [ 'type' => 'unit_moved', 'player' => 123 ] );
See Redis & Pub/Sub for full Redis documentation.
aca(): APCuCacheAdapter
Returns the APCu cache adapter singleton. APCu is an in-process memory cache — reads and writes are faster than Redis because there's no network round trip, but data is local to the current PHP process and not shared between servers:
aca()->set( 'config:feature_flags', $flags, 300 );
aca()->get( 'config:feature_flags' );
aca()->deleteByKeyPattern( 'config:*' );
aca()->clear();
APCu is best for data that's read frequently, changes rarely, and doesn't need to be shared across multiple application servers — route lists, configuration values, lookup tables.
mca(): MemcachedCacheAdapter
Returns the Memcached cache adapter singleton. Same PSR-16 interface as the other adapters with one limitation — deleteByKeyPattern() always throws UnsupportedPlatformException because Memcached doesn't support key enumeration:
mca()->set( 'session:data', $data, 1800 );
mca()->get( 'session:data' );
mca()->clear();
// This always throws UnsupportedPlatformException
mca()->deleteByKeyPattern( 'session:*' );
rc(): Predis\Client
Returns the raw Predis client. Use for Redis commands not covered by the cache adapter — sorted sets, lists, TTL checks, key existence, pub/sub subscriptions:
// Sorted set (leaderboard)
rc()->zadd( 'leaderboard', [ 'player:123' => 4500 ] );
rc()->zrevrange( 'leaderboard', 0, 9, [ 'WITHSCORES' => true ] );
// List (event log)
rc()->rpush( 'event_log', [ json_encode( $event ) ] );
rc()->lrange( 'event_log', 0, -1 );
// TTL check
rc()->ttl( 'my_key' );
// Key existence
rc()->exists( 'my_key' );
// Subscribe (blocking — use in console commands only)
rc()->subscribe( 'game:events', function( $redis, $channel, $message ): void {
// handle message
} );
rp(): Predis\Pipeline\Pipeline
Returns the Redis pipeline. Commands queued on the pipeline are batched and sent to Redis in a single round trip at the end of the request via the shutdown function registered in the framework kernel. Use for write-heavy operations where you don't need the result immediately:
// All three commands sent in one round trip at request end
rp()->set( 'player:123:last_seen', time() );
rp()->incr( 'player:123:login_count' );
rp()->rpush( 'player:123:session_log', [ json_encode( $sessionData ) ] );
Never use the pipeline for reads — pipelined commands return PendingResponse objects until execute() is called at shutdown. Use rc() or rca() for reads.
s(): Symfony\Component\HttpFoundation\Session\Session
Returns the Symfony session singleton:
// Store
s()->set( 'selected_map', $mapId );
// Retrieve with default
s()->get( 'selected_map', 'default_map' );
// Check existence
s()->has( 'selected_map' );
// Remove
s()->remove( 'selected_map' );
// Clear entire session
s()->clear();
r(): Symfony\Component\HttpFoundation\Request
Returns the current HTTP request object. Available everywhere during the request lifecycle — controllers, middleware, views, event listeners:
// Get client IP
$ip = r()->getClientIp();
// Read a POST field
$email = r()->request->get( 'email' );
// Read a query parameter
$page = (int) r()->query->get( 'page', 1 );
// Check HTTP method
if ( r()->isMethod( 'POST' ) ) { ... }
// Read a header
$token = r()->headers->get( 'X-Api-Token' );
r() delegates to Router::getRequest(), which holds the request set at the start of routing. It is guaranteed to be set for any code that runs inside the PHP-SF request lifecycle (controllers, middleware, RedirectTrait, event listeners). Do not call it during container build or before routing starts.
j_encode( mixed $value, int $flags = JSON_THROW_ON_ERROR, int $depth = 512 ): string|false
Wraps json_encode() with JSON_THROW_ON_ERROR as default. Throws JsonException on failure instead of returning false silently:
$json = j_encode( [ 'player' => 123, 'action' => 'build' ] );
// With additional flags
$json = j_encode( $data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE );
j_decode( string $json, bool $associative = false, int $depth = 512, int $flags = JSON_THROW_ON_ERROR ): mixed
Wraps json_decode() with JSON_THROW_ON_ERROR as default. Returns an object by default, array when $associative = true:
// Returns stdClass object
$data = j_decode( $json );
// Returns associative array
$data = j_decode( $json, true );
$playerId = j_decode( $json, true )['player_id'];
csrf_token( bool $asInput = false ): string
Returns the per-session CSRF token, generating and storing it in the session on first call. Pass $asInput = true to receive a ready-to-embed <input> element instead of the raw token string.
Used in every PHP view template that contains a mutating form — the csrf global middleware validates _token automatically on all POST, PUT, PATCH, and DELETE requests:
// Embed the hidden input (most common usage)
<form method="POST" action="<?= routeLink( 'user_store' ) ?>">
<?= csrf_token( asInput: true ) ?>
<!-- other fields -->
</form>
// Get the raw token (for JS / fetch)
const token = "<?= csrf_token() ?>";
fetch( '/api/endpoint', {
method: 'POST',
headers: { 'X-CSRF-Token': token },
body: JSON.stringify( data ),
} );
The token is stored under the _csrf_token session key and persists for the lifetime of the session. It is validated by the csrf middleware — see Middleware for the full CSRF setup and how to opt routes out using no_csrf.
user(): User|false
Returns the currently authenticated user entity, or false if not authenticated. Shorthand for auth::user():
if ( user() !== false ) {
$playerId = user()->getId();
$playerLogin = user()->getLogin();
}
// Common pattern — guard then use
if ( auth::isAuthenticated() === false )
return $this->redirectTo( 'login_page' );
$profile = user()->getProfile();
The return type is your application's User class (whatever was registered via setApplicationUserClassName()), typed as User|false.
All five functions share the same tokeniser (StringCase::words()). The input is split into words by detecting every type of word boundary, then the words are rejoined in the target format. Word boundaries detected:
helloWorld → hello, WorldXMLHttp → XML, Httpuser1Profile → user1, Profile_, -, ., /, \, whitespace (including runs of multiple separators)Unicode letters are handled correctly (\p{Ll} / \p{Lu} patterns). Accents are preserved — caféUser → café_user.
string_to_snake( string $input ): string
Converts any string format to snake_case. Empty string returns empty string:
string_to_snake( 'helloWorld' ) // 'hello_world'
string_to_snake( 'XMLHttpRequest' ) // 'xml_http_request'
string_to_snake( 'user1Profile' ) // 'user1_profile'
string_to_snake( 'Hello-World' ) // 'hello_world'
string_to_snake( 'foo.bar/baz' ) // 'foo_bar_baz'
string_to_screaming_snake( string $input ): string
Converts any string format to SCREAMING_SNAKE_CASE. Same tokeniser as string_to_snake(), result uppercased:
string_to_screaming_snake( 'helloWorld' ) // 'HELLO_WORLD'
string_to_screaming_snake( 'XMLHttpRequest' ) // 'XML_HTTP_REQUEST'
string_to_screaming_snake( 'version2API' ) // 'VERSION2_API'
string_to_camel( string $input ): string
Converts any string format to camelCase. The first word stays lowercase; subsequent words are title-cased via mb_convert_case( MB_CASE_TITLE ). Note: MB_CASE_TITLE treats digits as word boundaries, so a digit inside a later word will capitalise the letter that follows it:
string_to_camel( 'hello_world' ) // 'helloWorld'
string_to_camel( 'XMLHttpRequest' ) // 'xmlHttpRequest'
string_to_camel( 'version2API' ) // 'version2Api'
string_to_camel( 'USER_ID' ) // 'userId'
string_to_pascal( string $input ): string
Converts any string format to PascalCase. Every word (including the first) is title-cased via mb_convert_case( MB_CASE_TITLE ):
string_to_pascal( 'hello_world' ) // 'HelloWorld'
string_to_pascal( 'XMLHttpRequest' ) // 'XmlHttpRequest'
string_to_pascal( 'version2API' ) // 'Version2Api'
string_to_pascal( 'user_id' ) // 'UserId'
string_to_kebab( string $input ): string
Converts any string format to kebab-case. Same tokeniser as string_to_snake(), words joined with -:
string_to_kebab( 'helloWorld' ) // 'hello-world'
string_to_kebab( 'XMLHttpRequest' ) // 'xml-http-request'
string_to_kebab( 'user1Profile' ) // 'user1-profile'
string_to_kebab( 'foo_bar_baz' ) // 'foo-bar-baz'
The following functions are deprecated since v2.1.2 and will be removed in v3. Replace them with the equivalents above.
camel_to_snake( string $input ): string // @deprecated — use string_to_snake()
snakeToCamel( string $input ): string // @deprecated — use string_to_camel()
env( string $name, string|null $default = null ): string|null
Returns an environment variable value from $_ENV, or $default if not set. Returns null if not set and no default provided:
env( 'APP_ENV' ) // 'dev'
env( 'REDIS_CACHE_URL' ) // 'redis://localhost:6379/0'
env( 'MISSING_VAR' ) // null
env( 'MISSING_VAR', 'fallback' ) // 'fallback'
This reads from $_ENV which is populated by Dotenv::bootEnv() during bootstrap. It does not read from $_SERVER or getenv().
project_dir(): string
Returns the absolute path to the project root directory:
project_dir()
// '/var/www/nations-original'
// Common usage
$configPath = project_dir() . '/config/constants.php';
$sqlFile = project_dir() . '/Doctrine/fixtures/my_function.sql';
Delegates to App\Kernel::getInstance()->getProjectDir().
time_inc( int $seconds ): string
Returns a datetime string for the current time plus the given number of seconds. Useful for setting future expiry times:
time_inc( 3600 ) // '2026-03-20 13:00:00' (1 hour from now)
time_inc( 86400 ) // '2026-03-21 12:00:00' (1 day from now)
time_dec( int $seconds ): string
Returns a datetime string for the current time minus the given number of seconds:
time_dec( 3600 ) // '2026-03-20 11:00:00' (1 hour ago)
time_dec( 86400 ) // '2026-03-19 12:00:00' (1 day ago)
get_time_diff_in_seconds( DateTimeInterface|string $time ): int
Returns the difference in seconds between the given time and now. Positive values mean the time is in the future, negative means past:
get_time_diff_in_seconds( '2026-03-21 12:00:00' ) // 86400 (1 day from now)
get_time_diff_in_seconds( '2026-03-19 12:00:00' ) // -86400 (1 day ago)
get_time_diff_in_seconds( $entity->getCreatedAt() ) // seconds since entity was created
get_time_diff( DateTimeInterface|string $time, DateTimeZone $timezone = null ): string
Returns a human-readable time difference string between the given time and now. Uses the translation system for unit labels — yr, mo, d, hr, min, sec will be added to your locale files:
get_time_diff( '2026-03-19 12:00:00' ) // '1 d ago' / '1 day ago' (translated)
get_time_diff( '2025-03-20 12:00:00' ) // '1 yr '
get_time_diff( $post->getCreatedAt() ) // '5 min '
get_time_diff( $user->getCreatedAt() ) // '2 mos '
Returns the moment_ago translation key string when the difference is less than one second.
showTimeDiff( DateTimeInterface|string $time, DateTimeZone $timezone = null ): void
Echoes the result of getTimeDiff() directly. Convenience wrapper for use inside views:
// In a view
<span class="timestamp">
<?php showTimeDiff( $post->getCreatedAt() ) ?>
</span>
get_time_diff_from_seconds( int $seconds ): string
Converts a number of seconds into a human-readable duration string. Internally calls getTimeDiff( timeDec( $seconds ) ):
get_time_diff_from_seconds( 90 ) // '1 min '
get_time_diff_from_seconds( 3600 ) // '1 hr '
get_time_diff_from_seconds( 86400 ) // '1 d '
show_time_from_seconds( int $seconds ): void
Echoes the result of getTimeFromSeconds(). Convenience wrapper for views:
<span>Cooldown: <?php show_time_from_seconds( $ability->getCooldown() ) ?></span>
| Function | Returns | Purpose |
|---|---|---|
em( $connection ) |
EntityManager |
Doctrine entity manager |
qb( $connection ) |
QueryBuilder |
Fresh query builder |
ca() |
AbstractCacheAdapter |
Best available cache adapter |
rca() |
RedisCacheAdapter |
Redis cache adapter |
aca() |
APCuCacheAdapter |
APCu cache adapter |
mca() |
MemcachedCacheAdapter |
Memcached cache adapter |
rc() |
Predis\Client |
Raw Redis client |
rp() |
Predis\Pipeline |
Redis pipeline |
s() |
Session |
Symfony session |
r() |
Request |
Current HTTP request |
csrf_token() |
string |
CSRF token (or hidden input when $asInput = true) |
j_encode() |
string |
JSON encode with throw on error |
j_decode() |
mixed |
JSON decode with throw on error |
user() |
User\|false |
Current authenticated user |
string_to_snake() |
string |
Any format → snake_case |
string_to_screaming_snake() |
string |
Any format → SCREAMING_SNAKE_CASE |
string_to_camel() |
string |
Any format → camelCase |
string_to_pascal() |
string |
Any format → PascalCase |
string_to_kebab() |
string |
Any format → kebab-case |
camel_to_snake() |
string |
Deprecated — use string_to_snake() |
snakeToCamel() |
string |
Deprecated — use string_to_camel() |
env() |
string\|null |
Environment variable |
project_dir() |
string |
Absolute project root path |
timeInc() |
string |
Datetime string N seconds in future |
timeDec() |
string |
Datetime string N seconds in past |
getTimeDiffInSeconds() |
int |
Seconds between time and now |
getTimeDiff() |
string |
Human-readable time difference |
showTimeDiff() |
void |
Echo human-readable time difference |
getTimeFromSeconds() |
string |
Human-readable duration from seconds |
showTimeFromSeconds() |
void |
Echo human-readable duration |