1# PHP {#sec-php}
2
3## User Guide {#ssec-php-user-guide}
4
5### Overview {#ssec-php-user-guide-overview}
6
7Several versions of PHP are available on Nix, each of which having a
8wide variety of extensions and libraries available.
9
10The different versions of PHP that nixpkgs provides are located under
11attributes named based on major and minor version number; e.g.,
12`php81` is PHP 8.1.
13
14Only versions of PHP that are supported by upstream for the entirety
15of a given NixOS release will be included in that release of
16NixOS. See [PHP Supported
17Versions](https://www.php.net/supported-versions.php).
18
19The attribute `php` refers to the version of PHP considered most
20stable and thoroughly tested in nixpkgs for any given release of
21NixOS - not necessarily the latest major release from upstream.
22
23All available PHP attributes are wrappers around their respective
24binary PHP package and provide commonly used extensions this way. The
25real PHP 8.1 package, i.e. the unwrapped one, is available as
26`php81.unwrapped`; see the next section for more details.
27
28Interactive tools built on PHP are put in `php.packages`; composer is
29for example available at `php.packages.composer`.
30
31Most extensions that come with PHP, as well as some popular
32third-party ones, are available in `php.extensions`; for example, the
33opcache extension shipped with PHP is available at
34`php.extensions.opcache` and the third-party ImageMagick extension at
35`php.extensions.imagick`.
36
37### Installing PHP with extensions {#ssec-php-user-guide-installing-with-extensions}
38
39A PHP package with specific extensions enabled can be built using
40`php.withExtensions`. This is a function which accepts an anonymous
41function as its only argument; the function should accept two named
42parameters: `enabled` - a list of currently enabled extensions and
43`all` - the set of all extensions, and return a list of wanted
44extensions. For example, a PHP package with all default extensions and
45ImageMagick enabled:
46
47```nix
48php.withExtensions ({ enabled, all }: enabled ++ [ all.imagick ])
49```
50
51To exclude some, but not all, of the default extensions, you can
52filter the `enabled` list like this:
53
54```nix
55php.withExtensions (
56 { enabled, all }: (lib.filter (e: e != php.extensions.opcache) enabled) ++ [ all.imagick ]
57)
58```
59
60To build your list of extensions from the ground up, you can
61ignore `enabled`:
62
63```nix
64php.withExtensions (
65 { all, ... }:
66 with all;
67 [
68 imagick
69 opcache
70 ]
71)
72```
73
74`php.withExtensions` provides extensions by wrapping a minimal php
75base package, providing a `php.ini` file listing all extensions to be
76loaded. You can access this package through the `php.unwrapped`
77attribute; useful if you, for example, need access to the `dev`
78output. The generated `php.ini` file can be accessed through the
79`php.phpIni` attribute.
80
81If you want a PHP build with extra configuration in the `php.ini`
82file, you can use `php.buildEnv`. This function takes two named and
83optional parameters: `extensions` and `extraConfig`. `extensions`
84takes an extension specification equivalent to that of
85`php.withExtensions`, `extraConfig` a string of additional `php.ini`
86configuration parameters. For example, a PHP package with the opcache
87and ImageMagick extensions enabled, and `memory_limit` set to `256M`:
88
89```nix
90php.buildEnv {
91 extensions =
92 { all, ... }:
93 with all;
94 [
95 imagick
96 opcache
97 ];
98 extraConfig = "memory_limit=256M";
99}
100```
101
102#### Example setup for `phpfpm` {#ssec-php-user-guide-installing-with-extensions-phpfpm}
103
104You can use the previous examples in a `phpfpm` pool called `foo` as
105follows:
106
107```nix
108let
109 myPhp = php.withExtensions (
110 { all, ... }:
111 with all;
112 [
113 imagick
114 opcache
115 ]
116 );
117in
118{
119 services.phpfpm.pools."foo".phpPackage = myPhp;
120}
121```
122
123```nix
124let
125 myPhp = php.buildEnv {
126 extensions =
127 { all, ... }:
128 with all;
129 [
130 imagick
131 opcache
132 ];
133 extraConfig = "memory_limit=256M";
134 };
135in
136{
137 services.phpfpm.pools."foo".phpPackage = myPhp;
138}
139```
140
141#### Example usage with `nix-shell` {#ssec-php-user-guide-installing-with-extensions-nix-shell}
142
143This brings up a temporary environment that contains a PHP interpreter
144with the extensions `imagick` and `opcache` enabled:
145
146```sh
147nix-shell -p 'php.withExtensions ({ all, ... }: with all; [ imagick opcache ])'
148```
149
150### Installing PHP packages with extensions {#ssec-php-user-guide-installing-packages-with-extensions}
151
152All interactive tools use the PHP package you get them from, so all
153packages at `php.packages.*` use the `php` package with its default
154extensions. Sometimes this default set of extensions isn't enough and
155you may want to extend it. A common case of this is the `composer`
156package: a project may depend on certain extensions and `composer`
157won't work with that project unless those extensions are loaded.
158
159Example of building `composer` with additional extensions:
160
161```nix
162(php.withExtensions (
163 { all, enabled }:
164 enabled
165 ++ (with all; [
166 imagick
167 redis
168 ])
169)).packages.composer
170```
171
172### Overriding PHP packages {#ssec-php-user-guide-overriding-packages}
173
174`php-packages.nix` form a scope, allowing us to override the packages defined
175within. For example, to apply a patch to a `mysqlnd` extension, you can
176pass an overlay-style function to `php`’s `packageOverrides` argument:
177
178```nix
179php.override {
180 packageOverrides = final: prev: {
181 extensions = prev.extensions // {
182 mysqlnd = prev.extensions.mysqlnd.overrideAttrs (attrs: {
183 patches = attrs.patches or [ ] ++ [
184 # ...
185 ];
186 });
187 };
188 };
189}
190```
191
192### Building PHP projects {#ssec-building-php-projects}
193
194With [Composer](https://getcomposer.org/), you can effectively build PHP
195projects by streamlining dependency management. As the de-facto standard
196dependency manager for PHP, Composer enables you to declare and manage the
197libraries your project relies on, ensuring a more organized and efficient
198development process.
199
200Composer is not a package manager in the same sense as `Yum` or `Apt` are. Yes,
201it deals with "packages" or libraries, but it manages them on a per-project
202basis, installing them in a directory (e.g. `vendor`) inside your project. By
203default, it does not install anything globally. This idea is not new and
204Composer is strongly inspired by node's `npm` and ruby's `bundler`.
205
206Currently, there is no other PHP tool that offers the same functionality as
207Composer. Consequently, incorporating a helper in Nix to facilitate building
208such applications is a logical choice.
209
210In a Composer project, dependencies are defined in a `composer.json` file,
211while their specific versions are locked in a `composer.lock` file. Some
212Composer-based projects opt to include this `composer.lock` file in their source
213code, while others choose not to.
214
215In Nix, there are multiple approaches to building a Composer-based project.
216
217One such method is the `php.buildComposerProject2` helper function, which serves
218as a wrapper around `mkDerivation`.
219
220Using this function, you can build a PHP project that includes both a
221`composer.json` and `composer.lock` file. If the project specifies binaries
222using the `bin` attribute in `composer.json`, these binaries will be
223automatically linked and made accessible in the derivation. In this context,
224"binaries" refer to PHP scripts that are intended to be executable.
225
226To use the helper effectively, add the `vendorHash` attribute, which
227enables the wrapper to handle the heavy lifting.
228
229Internally, the helper operates in three stages:
230
2311. It constructs a `composerRepository` attribute derivation by creating a
232 composer repository on the filesystem containing dependencies specified in
233 `composer.json`. This process uses the function
234 `php.mkComposerRepository` which in turn uses the
235 `php.composerHooks.composerRepositoryHook` hook. Internally this function uses
236 a custom
237 [Composer plugin](https://github.com/nix-community/composer-local-repo-plugin) to
238 generate the repository.
2392. The resulting `composerRepository` derivation is then used by the
240 `php.composerHooks.composerInstallHook` hook, which is responsible for
241 creating the final `vendor` directory.
2423. Any "binary" specified in the `composer.json` are linked and made accessible
243 in the derivation.
244
245As the autoloader optimization can be activated directly within the
246`composer.json` file, we do not enable any autoloader optimization flags.
247
248To customize the PHP version, you can specify the `php` attribute. Similarly, if
249you wish to modify the Composer version, use the `composer` attribute. It is
250important to note that both attributes should be of the `derivation` type.
251
252Here's an example of working code example using `php.buildComposerProject2`:
253
254```nix
255{ php, fetchFromGitHub }:
256
257php.buildComposerProject2 (finalAttrs: {
258 pname = "php-app";
259 version = "1.0.0";
260
261 src = fetchFromGitHub {
262 owner = "git-owner";
263 repo = "git-repo";
264 tag = finalAttrs.version;
265 hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
266 };
267
268 # PHP version containing the `ast` extension enabled
269 php = php.buildEnv {
270 extensions = (
271 { enabled, all }:
272 enabled
273 ++ (with all; [
274 ast
275 ])
276 );
277 };
278
279 # The composer vendor hash
280 vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
281
282 # If the composer.lock file is missing from the repository, add it:
283 # composerLock = ./path/to/composer.lock;
284})
285```
286
287In case the file `composer.lock` is missing from the repository, it is possible
288to specify it using the `composerLock` attribute.
289
290The other method is to use all these methods and hooks individually. This has
291the advantage of building a PHP library within another derivation very easily
292when necessary.
293
294Here's a working code example to build a PHP library using `mkDerivation` and
295separate functions and hooks:
296
297```nix
298{
299 stdenvNoCC,
300 fetchFromGitHub,
301 php,
302}:
303
304stdenvNoCC.mkDerivation (
305 finalAttrs:
306 let
307 src = fetchFromGitHub {
308 owner = "git-owner";
309 repo = "git-repo";
310 rev = finalAttrs.version;
311 hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
312 };
313 in
314 {
315 inherit src;
316 pname = "php-app";
317 version = "1.0.0";
318
319 buildInputs = [ php ];
320
321 nativeBuildInputs = [
322 php.packages.composer
323 # This hook will use the attribute `composerRepository`
324 php.composerHooks.composerInstallHook
325 ];
326
327 composerRepository = php.mkComposerRepository {
328 inherit (finalAttrs) pname version src;
329 composerNoDev = true;
330 composerNoPlugins = true;
331 composerNoScripts = true;
332 # Specifying a custom composer.lock since it is not present in the sources.
333 composerLock = ./composer.lock;
334 # The composer vendor hash
335 vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
336 };
337 }
338)
339```