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```