Skip to main content

Installation

On this page

Requires PHP 8.4+. Pick the method matching your workflow.

Which method?#

GoalUse
New project with tests + scriptsComposer skeleton
Add to existing Composer projectComposer require
Run a single file, no setupPHAR
No PHP installed (Docker only)Docker
Reproducible dev shellsNix
Fastest pathGetting Started

New project from skeleton#

Ships with tests, build config, ready-to-use composer scripts (repl, dev, test, build, format).

composer create-project --stability dev phel-lang/cli-skeleton example-app
cd example-app
composer repl

Add to an existing project#

composer require phel-lang/phel-lang
vendor/bin/phel init my-app    # scaffold phel-config.php + src/

All commands then via vendor/bin/phel <cmd> (e.g. vendor/bin/phel repl).

PHP Does this replace my PHP app?

No. Phel lives alongside PHP. require 'vendor/autoload.php' and call compiled Phel namespaces from PHP, or call PHP from Phel. Drop into any Composer project (Laravel, Symfony, WordPress plugin) and use where Lisp fits better.

PHAR (no project setup)#

Run without Composer. Good for quick experiments, CI one-shots, trying the language.

curl -L https://phel-lang.org/phar -o phel.phar
php phel.phar --version

Every command works the same:

php phel.phar repl
php phel.phar run src/main.phel
php phel.phar test --filter foo

Make it globally available:

chmod +x phel.phar
sudo mv phel.phar /usr/local/bin/phel
phel repl

Docker (no PHP required)#

No PHP installed? With Docker, run Phel in one command.

Zero-setup REPL#

Paste and you're in a live Phel REPL. No files, no install:

docker run --rm -it php:8.4-cli sh -c \
  "curl -sL https://phel-lang.org/phar -o /tmp/phel.phar && php /tmp/phel.phar repl"

Container downloads PHAR fresh each run. Fine for experimenting, wasteful for daily use. See Persistent phel alias for a cached setup.

Run a Phel file from your host#

Mount cwd, run any Phel script:

docker run --rm -it -v "$PWD":/app -w /app php:8.4-cli sh -c \
  "curl -sL https://phel-lang.org/phar -o /tmp/phel.phar && php /tmp/phel.phar run src/main.phel"

Persistent phel alias backed by Docker#

Download PHAR once, make phel feel native:

curl -L https://phel-lang.org/phar -o phel.phar

# Add to ~/.zshrc, ~/.bashrc, or run in your shell:
alias phel='docker run --rm -it -v "$PWD":/app -w /app php:8.4-cli php /app/phel.phar'

phel repl
phel run src/main.phel
phel test

Composer project with no local PHP#

Use official composer image (ships PHP + Composer):

docker run --rm -it -v "$PWD":/app -w /app composer \
  create-project --stability dev phel-lang/cli-skeleton example-app

cd example-app

# Start the REPL
docker run --rm -it -v "$PWD":/app -w /app -p 2345:2345 composer composer repl

Alias for daily use:

alias dcomposer='docker run --rm -it -v "$PWD":/app -w /app composer'
dcomposer composer repl
dcomposer composer test
dcomposer composer dev

-p 2345:2345 exposes default nREPL port for host editor integration. Omit if not needed.

Nix#

Reproducible dev environments. Phel is in nixpkgs: see phel on search.nixos.org or the package source.

No Nix yet? Install via Determinate Systems installer or official installer.

Ad-hoc shell#

nix shell nixpkgs#phel
phel repl

Nixpkgs may lag latest. Check nix eval nixpkgs#phel.version. For newest, use Composer or PHAR.

Project shell.nix#

Pin PHP + Composer for the team:

{ pkgs ? import <nixpkgs> { } }:

pkgs.mkShell {
  packages = with pkgs; [
    php84
    php84Packages.composer
  ];
}

Then nix-shell and use Composer as normal.

Verify install#

Run the doctor:

vendor/bin/phel doctor    ; Composer
php phel.phar doctor      ; PHAR
phel doctor               ; Nix / global

Checks PHP extensions (json, mbstring, readline), writable cache dir, source layout. Tells you exactly what's missing.

Clojure Mental model for the toolchain

Mapping from lein/deps.edn:

ClojurePhel
deps.edn / project.cljcomposer.json + phel-config.php
lein new app foocomposer create-project … cli-skeleton foo
clj / lein replcomposer repl or phel repl
lein testcomposer test or phel test
uberjarphel build (compiles to PHP)
nREPLphel nrepl (bencode over TCP)

Editor integration: nREPL + LSP. See Editor Support.

Upgrading from 0.38#

composer require phel-lang/phel-lang:^0.39
./vendor/bin/phel cache:clear        # or: rm -rf .phel/cache

Cached PHP from earlier installs references renamed core types and fails to load otherwise. Rebuild downstream projects after upgrade.

Breaking changes in 0.39 (Clojure-aligned core type renames):

  • VariableAtom
  • UuidUUID
  • BigIntegerBigInt
  • RationalRatio
  • PhelFutureFuture
  • ExInfoExceptionExceptionInfo
  • LazyConsCons
  • Auto-refer: common Phel\Lang\* types resolve without (:use ...). Interface suffix dropped (e.g. (php/instanceof x LazySeq)). User (:use ...) still overrides.

Earlier upgrades (0.37):

  • PhelConfig setters replaced by immutable withX() chain; old setX() shims emit deprecation notices. See Configuration.
  • PhelConfig::forProject(ProjectLayout $layout = Flat, string $mainNamespace = ''): layout argument is first, Flat is the default.
  • Phel\Printer moved to Phel\Shared\Printer. Phel sources should (:use Phel.Shared.Printer.Printer); the old path no longer resolves.
  • Cross-module exceptions + CodeSnippet moved to Phel\Shared\Exceptions / Phel\Shared\Parser\ReadModel.
  • Runtime state (cache, REPL history, error log) now lives under .phel/. Override via withPhelDir('...') or the PHEL_DIR env var.

Next steps#