PHP

How Anvil serves Laravel, Statamic, Symfony and plain PHP projects, installs PHP versions and picks one per project.

What Anvil detects

TypeDetected by
Laravelartisan
Statamicartisan and statamic/cms in composer.json
Symfonybin/console and symfony/framework-bundle in composer.json
PHPcomposer.json, index.php or public/index.php

Caddy answers at <name>.test and hands PHP requests to PHP-FPM; there is no dev server. The web root is public/ if present, else the folder itself; set another folder inside the project in the application's Settings › Web root or with anvil app root <name> <dir>. A worktree serves the same folder of its own checkout. If the folder is missing, the address says so instead of serving the project folder. PHP-FPM runs one pool per PHP version in use, shared by its applications; an application with its own php.ini values gets its own pool (see php.ini settings).

Installing PHP

Settings › Languages › PHP › Install, or Install PHP 8.4 in the title bar before any PHP is installed. With the CLI:

anvil php install 8.4
anvil php list

Versions: 8.5, 8.4, 8.3, 8.2, 8.1. anvil php install <minor> installs the newest patch into ~/.anvil/php/<minor>. The first version installed becomes the default.

Builds are portable binaries (FPM and CLI) from static-php-cli, which publishes no checksums; HTTPS is the only check.

The first install of a version writes ~/.anvil/php/<minor>/php.ini; reinstalling (for a newer patch) keeps it:

memory_limit = 512M
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 120

Composer is fetched on first use (by composer or a template): the latest stable composer.phar from getcomposer.org, checked against its published SHA-256, kept in ~/.anvil/bin.

OPcache

PHP 8.5 has OPcache built in. For 8.1–8.4, Anvil downloads php.net's source of the same version, checks its SHA-256, and builds only OPcache into ~/.anvil/php/<minor>/ext/opcache.so, which PHP-FPM loads. This takes a few minutes and needs the Xcode command line tools (xcode-select --install). If the build can't run, the version installs without OPcache and Anvil says why.

For a version installed earlier: Add OPcache in Settings › Languages › PHP, or anvil php opcache 8.4. The row shows Adding OPcache… while building, Install command line tools if they're missing, and OPcache once present (also in anvil php list).

Every version serves with these settings:

opcache.enable = 1
opcache.enable_cli = 0
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
opcache.memory_consumption = 256

JIT is off. An opcache.* value set for every application (anvil php ini opcache.memory_consumption 512) overrides Anvil's.

Choosing the version

First match wins:

  1. Your pin: anvil app php myapp 8.3, or PHP version in the application's Settings tab.

  2. .anvil.toml, if that version is installed:

    php = "8.3"

    The value must be a version Anvil offers. If it isn't installed, the Overview offers to install it; until then the default is used.

  3. The default: anvil use 8.4, Make default in Settings › Languages › PHP, or Default PHP Version in the menu bar.

The Overview shows the source, e.g. PHP 8.3 (.anvil.toml) or PHP 8.4 (default). Under anvil start, restart it after a version change; with the desktop app open, changes apply immediately.

Running an application

No start command or port: Caddy and PHP-FPM serve it as soon as Anvil runs. Queue workers, Vite and the like are dev processes.

Anvil doesn't run composer install for an added project, only for templates (see New applications).

Database

A Laravel application reaches an Anvil MySQL or PostgreSQL service over 127.0.0.1. Create a database (anvil service create-db <service> <database>, or in the app) and set in .env:

KeyMySQLPostgreSQL
DB_CONNECTIONmysqlpgsql
DB_HOST127.0.0.1127.0.0.1
DB_PORTthe service's port (usually 3306)the service's port (usually 5432)
DB_DATABASEyour databaseyour database
DB_USERNAMErootpostgres
DB_PASSWORDemptyempty

The service's page shows these lines with its port, plus DATABASE_URL; picking a database there adds DB_DATABASE.

php.ini settings

anvil php ini memory_limit 1G              # every application
anvil app ini myapp max_execution_time 300 # only myapp
anvil app ini myapp max_execution_time     # clear it

In the app, Settings › Languages › PHP › Defaults for all applications has Memory limit, Upload limit and Max execution time (empty uses the values above). An application's Settings tab has the same fields plus Other PHP settings for any php.ini line. The app's upload limit also sets post_max_size.

These apply to PHP-FPM only. The terminal php reads ~/.anvil/php/<version>/php.ini.

In your terminal

Setup puts these on your PATH:

CommandWhat it runs
phpAnvil's PHP
composerAnvil's composer.phar with the folder's PHP, which is also first on PATH for Composer's scripts
laravelThe Laravel installer, installed into ~/.anvil/composer with composer global require laravel/installer on first use

Inside an application's folder (or below it) they use that application's PHP version; elsewhere the default, or the newest installed if there's no default. OPcache is off for the terminal php. anvil which php prints which binary runs in the current folder.

Caches and files

WhatWhere
Composer cache for templates~/.anvil/cache/composer
laravel global packages~/.anvil/composer
Terminal composerComposer's own home and cache
PHP-FPM log~/.anvil/log/php-<version>-fpm.log

Anvil writes nothing into a PHP project to serve it, except log trimming: while the app runs it trims large files in storage/logs and deletes old daily logs (can be turned off in Settings). For Laravel, Statamic and plain PHP, vendor/ shows as rebuildable in Storage.

New applications

anvil new laravel shop --starter livewire --db sqlite --pm pnpm --php 8.4
anvil new statamic blog
anvil new symfony api
anvil new php hello

Or New… (⌘N) in the app, under PHP.

TemplateCreated withOptions
laravellaravel/laravel, or laravel/<kit>-starter-kit--starter none|react|vue|svelte|livewire, --db sqlite or a MySQL or PostgreSQL service (mysql, pgsql, or the service's name), --pm npm|pnpm|bun, --php
statamicstatamic/statamic--pm, --php
symfonysymfony/skeleton--php
phpa public/index.php that prints the PHP version--php

--php takes an installed version; default otherwise, or the newest installed. Composer runs create-project without installing or scripts; Anvil then runs post-root-package-install, composer install, then post-create-project-cmd. With a database service, Laravel gets a database named after the application and its .env points at it. All templates take --git/--no-git and --https/--no-https; see New applications.

Differences from production

  • Extensions are fixed. Anvil can't add extensions (OPcache on 8.1–8.4 aside). A composer.json requiring a missing one (e.g. ext-intl) fails composer install's platform check. PHP 8.4 has bcmath, bz2, calendar, ctype, curl, dom, exif, fileinfo, filter, ftp, gd, gmp, iconv, mbstring, mysqlnd, openssl, pcntl, PDO with MySQL, PostgreSQL and SQLite, pgsql, Phar, posix, redis, session, SimpleXML, soap, sockets, sqlite3, tokenizer, xml, xmlreader, xmlwriter, zip and zlib. No Xdebug, intl, imagick or pcov. PHP 8.5 adds OPcache, lexbor and uri. php -m lists a version's extensions; on 8.1–8.4 it omits OPcache, which only PHP-FPM loads.
  • OPcache revalidates every file on every request (validate_timestamps = 1, revalidate_freq = 0).
  • php.ini starts from Anvil's values (see Installing PHP).
  • PHP-FPM starts workers on demand: up to 16 per version's shared pool, 8 per application pool.

Removing a version

Hover the version in Settings › Languages › PHP and click Uninstall, or anvil php uninstall 8.3. This deletes ~/.anvil/php/8.3, including its php.ini.

Anvil refuses while the version is the default ("PHP 8.3 is the default version — choose another default first") or pinned ("blog, shop use PHP 8.3 — switch them to another version first"); Settings then shows Pinned by with their names instead of Uninstall. Choose another default, and unpin: set PHP version back to the first entry (Default or From .anvil.toml), run anvil app php blog without a version, or pin another version.

On this page