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 }: 49 enabled ++ [ all.imagick ]) 50``` 51 52To exclude some, but not all, of the default extensions, you can 53filter the `enabled` list like this: 54 55```nix 56php.withExtensions ({ enabled, all }: 57 (lib.filter (e: e != php.extensions.opcache) enabled) 58 ++ [ all.imagick ]) 59``` 60 61To build your list of extensions from the ground up, you can 62ignore `enabled`: 63 64```nix 65php.withExtensions ({ all, ... }: with all; [ imagick opcache ]) 66``` 67 68`php.withExtensions` provides extensions by wrapping a minimal php 69base package, providing a `php.ini` file listing all extensions to be 70loaded. You can access this package through the `php.unwrapped` 71attribute; useful if you, for example, need access to the `dev` 72output. The generated `php.ini` file can be accessed through the 73`php.phpIni` attribute. 74 75If you want a PHP build with extra configuration in the `php.ini` 76file, you can use `php.buildEnv`. This function takes two named and 77optional parameters: `extensions` and `extraConfig`. `extensions` 78takes an extension specification equivalent to that of 79`php.withExtensions`, `extraConfig` a string of additional `php.ini` 80configuration parameters. For example, a PHP package with the opcache 81and ImageMagick extensions enabled, and `memory_limit` set to `256M`: 82 83```nix 84php.buildEnv { 85 extensions = { all, ... }: with all; [ imagick opcache ]; 86 extraConfig = "memory_limit=256M"; 87} 88``` 89 90#### Example setup for `phpfpm` {#ssec-php-user-guide-installing-with-extensions-phpfpm} 91 92You can use the previous examples in a `phpfpm` pool called `foo` as 93follows: 94 95```nix 96let 97 myPhp = php.withExtensions ({ all, ... }: with all; [ imagick opcache ]); 98in { 99 services.phpfpm.pools."foo".phpPackage = myPhp; 100}; 101``` 102 103```nix 104let 105 myPhp = php.buildEnv { 106 extensions = { all, ... }: with all; [ imagick opcache ]; 107 extraConfig = "memory_limit=256M"; 108 }; 109in { 110 services.phpfpm.pools."foo".phpPackage = myPhp; 111}; 112``` 113 114#### Example usage with `nix-shell` {#ssec-php-user-guide-installing-with-extensions-nix-shell} 115 116This brings up a temporary environment that contains a PHP interpreter 117with the extensions `imagick` and `opcache` enabled: 118 119```sh 120nix-shell -p 'php.withExtensions ({ all, ... }: with all; [ imagick opcache ])' 121``` 122 123### Installing PHP packages with extensions {#ssec-php-user-guide-installing-packages-with-extensions} 124 125All interactive tools use the PHP package you get them from, so all 126packages at `php.packages.*` use the `php` package with its default 127extensions. Sometimes this default set of extensions isn't enough and 128you may want to extend it. A common case of this is the `composer` 129package: a project may depend on certain extensions and `composer` 130won't work with that project unless those extensions are loaded. 131 132Example of building `composer` with additional extensions: 133 134```nix 135(php.withExtensions ({ all, enabled }: 136 enabled ++ (with all; [ imagick redis ])) 137).packages.composer 138``` 139 140### Overriding PHP packages {#ssec-php-user-guide-overriding-packages} 141 142`php-packages.nix` form a scope, allowing us to override the packages defined 143within. For example, to apply a patch to a `mysqlnd` extension, you can 144pass an overlay-style function to `php`’s `packageOverrides` argument: 145 146```nix 147php.override { 148 packageOverrides = final: prev: { 149 extensions = prev.extensions // { 150 mysqlnd = prev.extensions.mysqlnd.overrideAttrs (attrs: { 151 patches = attrs.patches or [] ++ [ 152 153 ]; 154 }); 155 }; 156 }; 157} 158``` 159 160### Building PHP projects {#ssec-building-php-projects} 161 162With [Composer](https://getcomposer.org/), you can effectively build PHP 163projects by streamlining dependency management. As the de-facto standard 164dependency manager for PHP, Composer enables you to declare and manage the 165libraries your project relies on, ensuring a more organized and efficient 166development process. 167 168Composer is not a package manager in the same sense as `Yum` or `Apt` are. Yes, 169it deals with "packages" or libraries, but it manages them on a per-project 170basis, installing them in a directory (e.g. `vendor`) inside your project. By 171default, it does not install anything globally. This idea is not new and 172Composer is strongly inspired by node's `npm` and ruby's `bundler`. 173 174Currently, there is no other PHP tool that offers the same functionality as 175Composer. Consequently, incorporating a helper in Nix to facilitate building 176such applications is a logical choice. 177 178In a Composer project, dependencies are defined in a `composer.json` file, 179while their specific versions are locked in a `composer.lock` file. Some 180Composer-based projects opt to include this `composer.lock` file in their source 181code, while others choose not to. 182 183In Nix, there are multiple approaches to building a Composer-based project. 184 185One such method is the `php.buildComposerProject` helper function, which serves 186as a wrapper around `mkDerivation`. 187 188Using this function, you can build a PHP project that includes both a 189`composer.json` and `composer.lock` file. If the project specifies binaries 190using the `bin` attribute in `composer.json`, these binaries will be 191automatically linked and made accessible in the derivation. In this context, 192"binaries" refer to PHP scripts that are intended to be executable. 193 194To use the helper effectively, add the `vendorHash` attribute, which 195enables the wrapper to handle the heavy lifting. 196 197Internally, the helper operates in three stages: 198 1991. It constructs a `composerRepository` attribute derivation by creating a 200 composer repository on the filesystem containing dependencies specified in 201 `composer.json`. This process uses the function 202 `php.mkComposerRepository` which in turn uses the 203 `php.composerHooks.composerRepositoryHook` hook. Internally this function uses 204 a custom 205 [Composer plugin](https://github.com/nix-community/composer-local-repo-plugin) to 206 generate the repository. 2072. The resulting `composerRepository` derivation is then used by the 208 `php.composerHooks.composerInstallHook` hook, which is responsible for 209 creating the final `vendor` directory. 2103. Any "binary" specified in the `composer.json` are linked and made accessible 211 in the derivation. 212 213As the autoloader optimization can be activated directly within the 214`composer.json` file, we do not enable any autoloader optimization flags. 215 216To customize the PHP version, you can specify the `php` attribute. Similarly, if 217you wish to modify the Composer version, use the `composer` attribute. It is 218important to note that both attributes should be of the `derivation` type. 219 220Here's an example of working code example using `php.buildComposerProject`: 221 222```nix 223{ php, fetchFromGitHub }: 224 225php.buildComposerProject (finalAttrs: { 226 pname = "php-app"; 227 version = "1.0.0"; 228 229 src = fetchFromGitHub { 230 owner = "git-owner"; 231 repo = "git-repo"; 232 rev = finalAttrs.version; 233 hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8="; 234 }; 235 236 # PHP version containing the `ast` extension enabled 237 php = php.buildEnv { 238 extensions = ({ enabled, all }: enabled ++ (with all; [ 239 ast 240 ])); 241 }; 242 243 # The composer vendor hash 244 vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ="; 245 246 # If the composer.lock file is missing from the repository, add it: 247 # composerLock = ./path/to/composer.lock; 248}) 249``` 250 251In case the file `composer.lock` is missing from the repository, it is possible 252to specify it using the `composerLock` attribute. 253 254The other method is to use all these methods and hooks individually. This has 255the advantage of building a PHP library within another derivation very easily 256when necessary. 257 258Here's a working code example to build a PHP library using `mkDerivation` and 259separate functions and hooks: 260 261```nix 262{ stdenvNoCC, fetchFromGitHub, php }: 263 264stdenvNoCC.mkDerivation (finalAttrs: 265let 266 src = fetchFromGitHub { 267 owner = "git-owner"; 268 repo = "git-repo"; 269 rev = finalAttrs.version; 270 hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8="; 271 }; 272in { 273 inherit src; 274 pname = "php-app"; 275 version = "1.0.0"; 276 277 buildInputs = [ php ]; 278 279 nativeBuildInputs = [ 280 php.packages.composer 281 # This hook will use the attribute `composerRepository` 282 php.composerHooks.composerInstallHook 283 ]; 284 285 composerRepository = php.mkComposerRepository { 286 inherit (finalAttrs) src; 287 # Specifying a custom composer.lock since it is not present in the sources. 288 composerLock = ./composer.lock; 289 # The composer vendor hash 290 vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ="; 291 }; 292}) 293```