97b0ae26f7
While the word 'simply' is usually added to encourage readers, it often has the opposite effect and may even appear condescending, especially when the reader runs into trouble trying to apply the suggestions from the documentation. It is almost always an improvement to simply drop the word from the sentence. (there are more possible improvements like this, we can apply those in separate PRs)
293 lines
10 KiB
Markdown
293 lines
10 KiB
Markdown
# PHP {#sec-php}
|
||
|
||
## User Guide {#ssec-php-user-guide}
|
||
|
||
### Overview {#ssec-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](https://www.php.net/supported-versions.php).
|
||
|
||
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 {#ssec-php-user-guide-installing-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:
|
||
|
||
```nix
|
||
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:
|
||
|
||
```nix
|
||
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`:
|
||
|
||
```nix
|
||
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`:
|
||
|
||
```nix
|
||
php.buildEnv {
|
||
extensions = { all, ... }: with all; [ imagick opcache ];
|
||
extraConfig = "memory_limit=256M";
|
||
}
|
||
```
|
||
|
||
#### Example setup for `phpfpm` {#ssec-php-user-guide-installing-with-extensions-phpfpm}
|
||
|
||
You can use the previous examples in a `phpfpm` pool called `foo` as
|
||
follows:
|
||
|
||
```nix
|
||
let
|
||
myPhp = php.withExtensions ({ all, ... }: with all; [ imagick opcache ]);
|
||
in {
|
||
services.phpfpm.pools."foo".phpPackage = myPhp;
|
||
};
|
||
```
|
||
|
||
```nix
|
||
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` {#ssec-php-user-guide-installing-with-extensions-nix-shell}
|
||
|
||
This brings up a temporary environment that contains a PHP interpreter
|
||
with the extensions `imagick` and `opcache` enabled:
|
||
|
||
```sh
|
||
nix-shell -p 'php.withExtensions ({ all, ... }: with all; [ imagick opcache ])'
|
||
```
|
||
|
||
### Installing PHP packages with extensions {#ssec-php-user-guide-installing-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:
|
||
|
||
```nix
|
||
(php.withExtensions ({ all, enabled }:
|
||
enabled ++ (with all; [ imagick redis ]))
|
||
).packages.composer
|
||
```
|
||
|
||
### Overriding PHP packages {#ssec-php-user-guide-overriding-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:
|
||
|
||
```nix
|
||
php.override {
|
||
packageOverrides = final: prev: {
|
||
extensions = prev.extensions // {
|
||
mysqlnd = prev.extensions.mysqlnd.overrideAttrs (attrs: {
|
||
patches = attrs.patches or [] ++ [
|
||
…
|
||
];
|
||
});
|
||
};
|
||
};
|
||
}
|
||
```
|
||
|
||
### Building PHP projects {#ssec-building-php-projects}
|
||
|
||
With [Composer](https://getcomposer.org/), 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:
|
||
|
||
1. It constructs a `composerRepository` attribute derivation by creating a
|
||
composer repository on the filesystem containing dependencies specified in
|
||
`composer.json`. This process uses the function
|
||
`php.mkComposerRepository` which in turn uses the
|
||
`php.composerHooks.composerRepositoryHook` hook. Internally this function uses
|
||
a custom
|
||
[Composer plugin](https://github.com/nix-community/composer-local-repo-plugin) to
|
||
generate the repository.
|
||
2. The resulting `composerRepository` derivation is then used by the
|
||
`php.composerHooks.composerInstallHook` hook, which is responsible for
|
||
creating the final `vendor` directory.
|
||
3. 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`:
|
||
|
||
```nix
|
||
{ 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:
|
||
|
||
```nix
|
||
{ 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) src;
|
||
# 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=";
|
||
};
|
||
})
|
||
```
|