Globals and constants#
Access PHP superglobals with php/ prefix and get:
(get php/$_SERVER "key") ; $_SERVER['key']
(get php/$GLOBALS "argv") ; $GLOBALS['argv']
PHP define constants accessed via php/CONSTANT_NAME:
(php/define "MY_SETTING" "My value") ; Calls PHP define('MY_SETTING', 'My value');
php/MY_SETTING ; => "My value"PHP Coming from PHP? ›
The php/ prefix gives you direct access to PHP's global scope:
// PHP
$_SERVER['key']
$GLOBALS['argv']
MY_SETTING
// Phel
(get php/$_SERVER "key")
(get php/$GLOBALS "argv")
php/MY_SETTING
Note: Use Phel's immutable data structures when possible. Only use PHP arrays when you need to interop with PHP libraries that expect them.
Calling PHP functions#
Add php/ prefix to any PHP function name:
(php/strlen "test") ; => 4
(php/date "l") ; => "Monday" (or whatever the current day is)PHP Coming from PHP? ›
Any PHP function can be called by adding the php/ prefix:
// PHP
strlen("test");
date("l");
array_map($fn, $array);
// Phel
(php/strlen "test")
(php/date "l")
(php/array_map fn array)
However, Phel provides functional equivalents for many operations. For example, use (count "test") instead of (php/strlen "test") when working with Phel data structures.
Namespaced PHP functions use full path after php/. Three equivalent forms accepted (added in 0.37, last two are backslash-free):
(php/Foo\Bar\baz) ; classic backslash form
(php/Foo.Bar/baz) ; dot-separated, slash before fn name
(php/Foo.Bar.baz) ; fully dot-separated
(php/Amp.trapSignal [(php/:: SIGINT) (php/:: SIGTERM)])
Capture into a Phel alias:
(def trap-signal php/\Amp.trapSignal)
(trap-signal [2 15])Interop shorthands#
Terse forms that expand to verbose php/*. Use whichever reads better.
| Shorthand | Expands to |
|---|---|
(ClassName. args) | (php/new ClassName args) |
| `(new ClassName args) | (php/new ClassName args) |
(.method obj args) | (php/-> obj (method args)) |
(.-field obj) | (php/-> obj field) |
(ClassName/method args) | (php/:: ClassName (method args)) |
ClassName/MEMBER | (php/:: ClassName MEMBER) |
(ns my.module
(:use DateTimeImmutable DateInterval))
(DateTimeImmutable. "2026-04-20") ; constructor (preferred)
(.format (DateTimeImmutable.) "Y-m-d") ; instance method
(.-s (DateInterval. "PT30S")) ; property
(DateTimeImmutable/createFromFormat "Y-m-d" "2026-04-20") ; static method
DateTimeImmutable/ATOM ; static constantClass instantiation#
Three equivalent forms - prefer ClassName. for imported classes:
(ns my.module
(:use DateTime DateTimeImmutable))
(DateTime.) ; => DateTime instance (ClassName. shorthand)
(DateTime. "now") ; => DateTime instance with arg
(new DateTime) ; also valid
(php/new DateTime) ; also valid
(php/new "\\DateTimeImmutable") ; instantiate from string (dynamic)PHP Coming from PHP? ›
// PHP
new DateTime();
new DateTime("now");
new \DateTimeImmutable();
// Phel - preferred shorthand
(DateTime.)
(DateTime. "now")
(DateTimeImmutable.)
Import classes with :use to use the short ClassName. form without repeating the namespace.
Method and property call#
(php/-> object (methodname expr*))
(php/-> object property)
Calls method or accesses property. Both methodname and property must be symbols, not evaluated values.
Chain multiple in one php/->. Each element evaluates on result of previous, enabling fluent chains or nested property access.
(ns my.module
(:use DateInterval)
(:use DateTimeImmutable)
(:use stdClass))
(def di (DateInterval. "PT30S"))
(.format di "%s seconds") ; => "30 seconds" (.method shorthand)
(php/-> di (format "%s seconds")) ; same, verbose form
(.-s di) ; => 30 (.-prop shorthand)
;; Chain multiple calls:
;; (new DateTimeImmutable("2024-03-10"))->modify("+1 day")->format("Y-m-d")
(-> (DateTimeImmutable. "2024-03-10")
(.modify "+1 day")
(.format "Y-m-d"))
;; php/-> also works and is required for chains mixing methods and properties:
(php/-> user profile (getDisplayName))
;; Nested property access:
(def address (stdClass.))
(def user (stdClass.))
(php/oset (php/-> address city) "Berlin")
(php/oset (php/-> user address) address)
(php/-> user address city) ; => "Berlin"PHP Coming from PHP? ›
The php/-> operator is similar to PHP's -> but allows chaining in a more functional style:
// PHP
$di->format("%s seconds");
$di->s;
(new DateTimeImmutable("2024-03-10"))->modify("+1 day")->format("Y-m-d");
$user->profile->getDisplayName();
// Phel - shorthand forms
(.format di "%s seconds")
(.-s di)
(-> (DateTimeImmutable. "2024-03-10") (.modify "+1 day") (.format "Y-m-d"))
(php/-> user profile (getDisplayName)) ; mixed chains need php/->
Method calls: (.method obj args) shorthand or (php/-> obj (method args)). Property access: (.-prop obj) or (php/-> obj prop). Mixed chains (method + property in one expression) use php/-> directly.
Clojure Coming from Clojure? ›
The php/-> operator is inspired by Clojure's thread-first macro ->, but specifically designed for PHP object method chaining.
Static method and property#
(php/:: class (methodname expr*))
(php/:: class property)
Same as above, but static.
(ns my.module
(:use DateTimeImmutable))
DateTimeImmutable/ATOM ; => "Y-m-d\TH:i:sP" (shorthand)
(php/:: DateTimeImmutable ATOM) ; verbose form
(DateTimeImmutable/createFromFormat "Y-m-d" "2020-03-22") ; shorthand
(php/:: DateTimeImmutable (createFromFormat "Y-m-d" "2020-03-22")) ; verbosePHP Coming from PHP? ›
The php/:: operator is equivalent to PHP's :: for static method and property access:
// PHP
DateTimeImmutable::ATOM;
DateTimeImmutable::createFromFormat("Y-m-d", "2020-03-22");
// Phel - shorthand forms
DateTimeImmutable/ATOM
(DateTimeImmutable/createFromFormat "Y-m-d" "2020-03-22")
Set object properties#
(php/oset (php/-> object property) value)
(php/oset (php/:: class property) value)
Set value on class/object property.
(def x (stdclass.))
(php/oset (php/-> x name) "foo")PHP Coming from PHP? ›
php/oset is the Phel equivalent of PHP's property assignment:
// PHP
$x = new stdClass();
$x->name = "foo";
// Phel
(def x (stdclass.))
(php/oset (php/-> x name) "foo")
Note: This mutates the PHP object. When possible, use Phel's immutable data structures instead.
Get PHP array value#
(php/aget arr index)
Equivalent: arr[index] ?? null.
(php/aget ["a" "b" "c"] 0) ; Evaluates to "a"
(php/aget (php/array "a" "b" "c") 1) ; Evaluates to "b"
(php/aget (php/array "a" "b" "c") 5) ; Evaluates to nilPHP Coming from PHP? ›
php/aget safely accesses PHP array elements:
// PHP
$arr[0] ?? null;
$arr[1] ?? null;
$arr[5] ?? null; // Returns null
// Phel
(php/aget arr 0)
(php/aget arr 1)
(php/aget arr 5) ; Returns nil
Important distinction:
- Use
php/agetfor PHP arrays (mutable) - Use
getfor Phel data structures (immutable vectors, maps)
Get nested PHP array value#
(php/aget-in arr path)
Resolves nested values via a sequence of keys/indexes. path is a sequential collection (e.g. vector). Missing step returns nil.
(def users
(php/array
"users"
(php/array
(php/array "name" "Alice")
(php/array "name" "Bob"))))
(php/aget-in users ["users" 1 "name"]) ; Evaluates to "Bob"
(php/aget-in
(php/array "meta" (php/array "status" "ok"))
["meta" "status"]) ; Evaluates to "ok"
(php/aget-in
(php/array "meta" (php/array "status" "ok"))
["meta" "missing"]) ; Evaluates to nilPHP Coming from PHP? ›
php/aget-in provides safe nested array access:
// PHP - manual nested access with null coalescing
$users['users'][1]['name'] ?? null;
$data['meta']['status'] ?? null;
$data['meta']['missing'] ?? null;
// Phel - clean path-based access
(php/aget-in users ["users" 1 "name"])
(php/aget-in data ["meta" "status"])
(php/aget-in data ["meta" "missing"]) ; Returns nil safely
This is similar to Phel's get-in for immutable data structures, but specifically for PHP arrays.
Set PHP array value#
(php/aset arr index value)
Equivalent: arr[index] = value.
PHP Coming from PHP? ›
php/aset mutates a PHP array in place:
// PHP
$arr[0] = "value";
// Phel
(php/aset arr 0 "value")
Important: This mutates the array. For immutable operations, use Phel's assoc on Phel data structures instead.
Set nested PHP array value#
(php/aset-in arr path value)
Creates or updates nested entries. Missing intermediate arrays are created.
(def data (php/array))
(php/aset-in data ["user" "profile" "name"] "Charlie")
(php/aget-in data ["user" "profile" "name"]) ; Evaluates to "Charlie"
;; Equivalent to $data['user']['profile']['name'] = 'Charlie';PHP Coming from PHP? ›
php/aset-in creates nested structures automatically:
// PHP - manual nested array creation
$data = [];
$data['user']['profile']['name'] = 'Charlie';
// Phel - automatic path creation
(def data (php/array))
(php/aset-in data ["user" "profile" "name"] "Charlie")
This is the mutable counterpart to Phel's assoc-in for immutable data structures.
Append PHP array value#
(php/apush arr value)
Equivalent: arr[] = value.
PHP Coming from PHP? ›
php/apush appends to a PHP array:
// PHP
$arr[] = "new value";
// Phel
(php/apush arr "new value")
For immutable operations, use conj on Phel vectors instead.
Unset PHP array value#
(php/aunset arr index)
Equivalent: unset(arr[index]).
PHP Coming from PHP? ›
php/aunset removes an element from a PHP array:
// PHP
unset($arr[0]);
// Phel
(php/aunset arr 0)
For immutable operations, use dissoc on Phel maps instead.
Unset nested PHP array value#
(php/aunset-in arr path)
Removes nested entry. Parent arrays remain untouched even if empty after.
(def data (php/array "user" (php/array "profile" (php/array "name" "Dora"))))
(php/aunset-in data ["user" "profile" "name"])
(php/aget-in data ["user" "profile" "name"]) ; Evaluates to nil
;; Equivalent to unset($data['user']['profile']['name']);PHP Coming from PHP? ›
php/aunset-in removes nested array elements:
// PHP
unset($data['user']['profile']['name']);
// Phel
(php/aunset-in data ["user" "profile" "name"])
Parent arrays remain intact even if they become empty after the unset.
__DIR__, __FILE__, *file*#
PHP magic constants __DIR__ and __FILE__ work but expand at PHP compile, pointing to the generated PHP file under .phel/cache.
For the original Phel source path, use *file* (absolute path of current Phel file). Combine with php/dirname for the source dir.
(println __DIR__) ; Directory name of the generated PHP file
(println __FILE__) ; Filename of the generated PHP file
(println (php/dirname *file*)) ; Directory of the original Phel file
(println *file*) ; Absolute path of the original filePHP Coming from PHP? ›
Important distinction:
// PHP magic constants
__DIR__ // Points to .phel/cache directory (generated PHP)
__FILE__ // Points to cached .php file
// Phel special var
*file* // Points to your actual .phel source file
Use *file* when you need to reference the original Phel source location, such as for loading resources relative to your source code.
Calling Phel from PHP#
Useful for integrating Phel into existing PHP apps. Load the Phel namespace after autoload.php.
Example: using-exported-phel-function.php
<?php
use Phel\Phel;
use PhelGenerated\CliSkeleton\Modules\AdderModule;
$projectRootDir = dirname(__DIR__);
require $projectRootDir . '/vendor/autoload.php';
Phel::run($projectRootDir, 'cli-skeleton.modules.adder-module');
$adder = new AdderModule();
$result = $adder->adder(1, 2, 3);
echo 'Result = ' . $result . PHP_EOL;
Two ways: manually, or via the export command.
Manually#
PhelCallerTrait calls any Phel function from a PHP class. Inject the trait, call callPhel.
<?php
use Phel\Interop\PhelCallerTrait;
class MyExistingClass {
use PhelCallerTrait;
public function myExistingMethod(...$arguments) {
return $this->callPhel(
'my.phel.namespace',
'phel-function-name',
...$arguments
);
}
}Using the export command#
phel export generates a wrapper class for all Phel functions marked export.
Add config to phel-config.php first:
<?php
return (new \Phel\Config\PhelConfig())
->withExportFromDirectories(['src'])
->withExportNamespacePrefix('PhelGenerated')
->withExportTargetDirectory('src/PhelGenerated')
;
Option details: Configuration.
Mark a function exported with metadata:
(defn my-function
{:export true}
[a b]
(+ a b))
phel export then generates a wrapper class in the target dir (here src/PhelGenerated). Use it from PHP to call Phel functions.