PHP
User Guide
Overview
Several versions of PHP are available on Nix, each of which having a wide variety of extensions and libraries available.
The different versions of PHP that nixpkgs provides are located under
attributes named based on major and minor version number; e.g.,
php81
is PHP 8.1.
Only versions of PHP that are supported by upstream for the entirety of a given NixOS release will be included in that release of NixOS. See PHP Supported Versions.
The attribute php
refers to the version of PHP considered most
stable and thoroughly tested in nixpkgs for any given release of
NixOS - not necessarily the latest major release from upstream.
All available PHP attributes are wrappers around their respective
binary PHP package and provide commonly used extensions this way. The
real PHP 8.1 package, i.e. the unwrapped one, is available as
php81.unwrapped
; see the next section for more details.
Interactive tools built on PHP are put in php.packages
; composer is
for example available at php.packages.composer
.
Most extensions that come with PHP, as well as some popular
third-party ones, are available in php.extensions
; for example, the
opcache extension shipped with PHP is available at
php.extensions.opcache
and the third-party ImageMagick extension at
php.extensions.imagick
.
Installing PHP with extensions
A PHP package with specific extensions enabled can be built using
php.withExtensions
. This is a function which accepts an anonymous
function as its only argument; the function should accept two named
parameters: enabled
- a list of currently enabled extensions and
all
- the set of all extensions, and return a list of wanted
extensions. For example, a PHP package with all default extensions and
ImageMagick enabled:
php.withExtensions ({ enabled, all }:
enabled ++ [ all.imagick ])
To exclude some, but not all, of the default extensions, you can
filter the enabled
list like this:
php.withExtensions ({ enabled, all }:
(lib.filter (e: e != php.extensions.opcache) enabled)
++ [ all.imagick ])
To build your list of extensions from the ground up, you can
ignore enabled
:
php.withExtensions ({ all, ... }: with all; [ imagick opcache ])
php.withExtensions
provides extensions by wrapping a minimal php
base package, providing a php.ini
file listing all extensions to be
loaded. You can access this package through the php.unwrapped
attribute; useful if you, for example, need access to the dev
output. The generated php.ini
file can be accessed through the
php.phpIni
attribute.
If you want a PHP build with extra configuration in the php.ini
file, you can use php.buildEnv
. This function takes two named and
optional parameters: extensions
and extraConfig
. extensions
takes an extension specification equivalent to that of
php.withExtensions
, extraConfig
a string of additional php.ini
configuration parameters. For example, a PHP package with the opcache
and ImageMagick extensions enabled, and memory_limit
set to 256M
:
php.buildEnv {
extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M";
}
Example setup for phpfpm
You can use the previous examples in a phpfpm
pool called foo
as
follows:
let
myPhp = php.withExtensions ({ all, ... }: with all; [ imagick opcache ]);
in {
services.phpfpm.pools."foo".phpPackage = myPhp;
}
let
myPhp = php.buildEnv {
extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M";
};
in {
services.phpfpm.pools."foo".phpPackage = myPhp;
}
Example usage with nix-shell
This brings up a temporary environment that contains a PHP interpreter
with the extensions imagick
and opcache
enabled:
nix-shell -p 'php.withExtensions ({ all, ... }: with all; [ imagick opcache ])'
Installing PHP packages with extensions
All interactive tools use the PHP package you get them from, so all
packages at php.packages.*
use the php
package with its default
extensions. Sometimes this default set of extensions isn't enough and
you may want to extend it. A common case of this is the composer
package: a project may depend on certain extensions and composer
won't work with that project unless those extensions are loaded.
Example of building composer
with additional extensions:
(php.withExtensions ({ all, enabled }:
enabled ++ (with all; [ imagick redis ]))
).packages.composer
Overriding PHP packages
php-packages.nix
form a scope, allowing us to override the packages defined
within. For example, to apply a patch to a mysqlnd
extension, you can
pass an overlay-style function to php
’s packageOverrides
argument:
php.override {
packageOverrides = final: prev: {
extensions = prev.extensions // {
mysqlnd = prev.extensions.mysqlnd.overrideAttrs (attrs: {
patches = attrs.patches or [] ++ [
# ...
];
});
};
};
}
Building PHP projects
With Composer, you can effectively build PHP projects by streamlining dependency management. As the de-facto standard dependency manager for PHP, Composer enables you to declare and manage the libraries your project relies on, ensuring a more organized and efficient development process.
Composer is not a package manager in the same sense as Yum
or Apt
are. Yes,
it deals with "packages" or libraries, but it manages them on a per-project
basis, installing them in a directory (e.g. vendor
) inside your project. By
default, it does not install anything globally. This idea is not new and
Composer is strongly inspired by node's npm
and ruby's bundler
.
Currently, there is no other PHP tool that offers the same functionality as Composer. Consequently, incorporating a helper in Nix to facilitate building such applications is a logical choice.
In a Composer project, dependencies are defined in a composer.json
file,
while their specific versions are locked in a composer.lock
file. Some
Composer-based projects opt to include this composer.lock
file in their source
code, while others choose not to.
In Nix, there are multiple approaches to building a Composer-based project.
One such method is the php.buildComposerProject
helper function, which serves
as a wrapper around mkDerivation
.
Using this function, you can build a PHP project that includes both a
composer.json
and composer.lock
file. If the project specifies binaries
using the bin
attribute in composer.json
, these binaries will be
automatically linked and made accessible in the derivation. In this context,
"binaries" refer to PHP scripts that are intended to be executable.
To use the helper effectively, add the vendorHash
attribute, which
enables the wrapper to handle the heavy lifting.
Internally, the helper operates in three stages:
- It constructs a
composerRepository
attribute derivation by creating a composer repository on the filesystem containing dependencies specified incomposer.json
. This process uses the functionphp.mkComposerRepository
which in turn uses thephp.composerHooks.composerRepositoryHook
hook. Internally this function uses a custom Composer plugin to generate the repository. - The resulting
composerRepository
derivation is then used by thephp.composerHooks.composerInstallHook
hook, which is responsible for creating the finalvendor
directory. - Any "binary" specified in the
composer.json
are linked and made accessible in the derivation.
As the autoloader optimization can be activated directly within the
composer.json
file, we do not enable any autoloader optimization flags.
To customize the PHP version, you can specify the php
attribute. Similarly, if
you wish to modify the Composer version, use the composer
attribute. It is
important to note that both attributes should be of the derivation
type.
Here's an example of working code example using php.buildComposerProject
:
{ php, fetchFromGitHub }:
php.buildComposerProject (finalAttrs: {
pname = "php-app";
version = "1.0.0";
src = fetchFromGitHub {
owner = "git-owner";
repo = "git-repo";
rev = finalAttrs.version;
hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
};
# PHP version containing the `ast` extension enabled
php = php.buildEnv {
extensions = ({ enabled, all }: enabled ++ (with all; [
ast
]));
};
# The composer vendor hash
vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
# If the composer.lock file is missing from the repository, add it:
# composerLock = ./path/to/composer.lock;
})
In case the file composer.lock
is missing from the repository, it is possible
to specify it using the composerLock
attribute.
The other method is to use all these methods and hooks individually. This has the advantage of building a PHP library within another derivation very easily when necessary.
Here's a working code example to build a PHP library using mkDerivation
and
separate functions and hooks:
{ stdenvNoCC, fetchFromGitHub, php }:
stdenvNoCC.mkDerivation (finalAttrs:
let
src = fetchFromGitHub {
owner = "git-owner";
repo = "git-repo";
rev = finalAttrs.version;
hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
};
in {
inherit src;
pname = "php-app";
version = "1.0.0";
buildInputs = [ php ];
nativeBuildInputs = [
php.packages.composer
# This hook will use the attribute `composerRepository`
php.composerHooks.composerInstallHook
];
composerRepository = php.mkComposerRepository {
inherit (finalAttrs) pname version src;
composerNoDev = true;
composerNoPlugins = true;
composerNoScripts = true;
# Specifying a custom composer.lock since it is not present in the sources.
composerLock = ./composer.lock;
# The composer vendor hash
vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
};
})