php.buildEnv: Provide a list of currently enabled extensions

Rework withExtensions / buildEnv to handle currently enabled
extensions better and make them compatible with override. They now
accept a function with the named arguments enabled and all, where
enabled is a list of currently enabled extensions and all is the set
of all extensions. This gives us several nice properties:

 - You always get the right version of the list of currently enabled
   extensions

 - Invocations chain

 - It works well with overridden PHP packages - you always get the
   correct versions of extensions

As a contrived example of what's possible, you can add ImageMagick,
then override the version and disable fpm, then disable cgi, and
lastly remove the zip extension like this:

{ pkgs ? (import <nixpkgs>) {} }:
with pkgs;

let
  phpWithImagick = php74.withExtensions ({ all, enabled }: enabled ++ [ all.imagick ]);

  phpWithImagickWithoutFpm743 = phpWithImagick.override {
    version = "7.4.3";
    sha256 = "wVF7pJV4+y3MZMc6Ptx21PxQfEp6xjmYFYTMfTtMbRQ=";
    fpmSupport = false;
  };

  phpWithImagickWithoutFpmZip743 = phpWithImagickWithoutFpm743.withExtensions (
    { enabled, all }:
      lib.filter (e: e != all.zip) enabled);

  phpWithImagickWithoutFpmZipCgi743 = phpWithImagickWithoutFpmZip743.override {
    cgiSupport = false;
  };
in
  phpWithImagickWithoutFpmZipCgi743
This commit is contained in:
talyz 2020-04-12 23:31:56 +02:00
parent abedfadd73
commit 2ba7926959
No known key found for this signature in database
GPG Key ID: 2DED2151F4671A2B
5 changed files with 68 additions and 47 deletions

View File

@ -30,7 +30,7 @@ opcache extension shipped with PHP is available at
`php.extensions.opcache` and the third-party ImageMagick extension at `php.extensions.opcache` and the third-party ImageMagick extension at
`php.extensions.imagick`. `php.extensions.imagick`.
The different versions of PHP that nixpkgs provides is located under The different versions of PHP that nixpkgs provides are located under
attributes named based on major and minor version number; e.g., attributes named based on major and minor version number; e.g.,
`php74` is PHP 7.4 with commonly used extensions installed, `php74` is PHP 7.4 with commonly used extensions installed,
`php74base` is the same PHP runtime without extensions. `php74base` is the same PHP runtime without extensions.
@ -39,28 +39,31 @@ attributes named based on major and minor version number; e.g.,
A PHP package with specific extensions enabled can be built using A PHP package with specific extensions enabled can be built using
`php.withExtensions`. This is a function which accepts an anonymous `php.withExtensions`. This is a function which accepts an anonymous
function as its only argument; the function should take one argument, function as its only argument; the function should accept two named
the set of all extensions, and return a list of wanted extensions. For parameters: `enabled` - a list of currently enabled extensions and
example, a PHP package with the opcache and ImageMagick extensions `all` - the set of all extensions, and return a list of wanted
enabled: extensions. For example, a PHP package with all default extensions and
ImageMagick enabled:
```nix ```nix
php.withExtensions (e: with e; [ imagick opcache ]) php.withExtensions ({ enabled, all }:
enabled ++ [ all.imagick ])
``` ```
Note that this will give you a package with _only_ opcache and To exclude some, but not all, of the default extensions, you can
ImageMagick, none of the other extensions which are enabled by default filter the `enabled` list like this:
in the `php` package will be available.
To enable building on a previous PHP package, the currently enabled
extensions are made available in its `enabledExtensions`
attribute. For example, to generate a package with all default
extensions enabled, except opcache, but with ImageMagick:
```nix ```nix
php.withExtensions (e: php.withExtensions ({ enabled, all }:
(lib.filter (e: e != php.extensions.opcache) php.enabledExtensions) (lib.filter (e: e != php.extensions.opcache) enabled)
++ [ e.imagick ]) ++ [ all.imagick ])
```
To build your list of extensions from the ground up, you can simply
ignore `enabled`:
```nix
php.withExtensions ({ all, ... }: with all; [ opcache imagick ])
``` ```
If you want a PHP build with extra configuration in the `php.ini` If you want a PHP build with extra configuration in the `php.ini`
@ -73,7 +76,7 @@ and ImageMagick extensions enabled, and `memory_limit` set to `256M`:
```nix ```nix
php.buildEnv { php.buildEnv {
extensions = e: with e; [ imagick opcache ]; extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M"; extraConfig = "memory_limit=256M";
} }
``` ```
@ -85,7 +88,7 @@ follows:
```nix ```nix
let let
myPhp = php.withExtensions (e: with e; [ imagick opcache ]); myPhp = php.withExtensions ({ all, ... }: with all; [ opcache imagick ]);
in { in {
services.phpfpm.pools."foo".phpPackage = myPhp; services.phpfpm.pools."foo".phpPackage = myPhp;
}; };
@ -94,7 +97,7 @@ in {
```nix ```nix
let let
myPhp = php.buildEnv { myPhp = php.buildEnv {
extensions = e: with e; [ imagick opcache ]; extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M"; extraConfig = "memory_limit=256M";
}; };
in { in {
@ -105,8 +108,8 @@ in {
##### Example usage with `nix-shell` ##### Example usage with `nix-shell`
This brings up a temporary environment that contains a PHP interpreter This brings up a temporary environment that contains a PHP interpreter
with the extensions `imagick` and `opcache` enabled. with the extensions `imagick` and `opcache` enabled:
```sh ```sh
nix-shell -p 'php.buildEnv { extensions = e: with e; [ imagick opcache ]; }' nix-shell -p 'php.withExtensions ({ all, ... }: with all; [ imagick opcache ])'
``` ```

View File

@ -135,18 +135,23 @@
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
Since this release there's an easy way to customize your PHP install to get a much smaller Since this release there's an easy way to customize your PHP
base PHP with only wanted extensions enabled. See the following snippet installing a smaller PHP install to get a much smaller base PHP with only wanted
with the extensions <literal>imagick</literal>, <literal>opcache</literal> and extensions enabled. See the following snippet installing a
smaller PHP with the extensions <literal>imagick</literal>,
<literal>opcache</literal>, <literal>pdo</literal> and
<literal>pdo_mysql</literal> loaded: <literal>pdo_mysql</literal> loaded:
<programlisting> <programlisting>
environment.systemPackages = [ environment.systemPackages = [
(pkgs.php.buildEnv { extensions = pp: with pp; [ (pkgs.php.withExtensions
imagick ({ all, ... }: with all; [
opcache imagick
pdo_mysql opcache
]; }) pdo
pdo_mysql
])
)
];</programlisting> ];</programlisting>
The default <literal>php</literal> attribute hasn't lost any extensions - The default <literal>php</literal> attribute hasn't lost any extensions -

View File

@ -7,7 +7,7 @@ let
fpm = config.services.phpfpm.pools.roundcube; fpm = config.services.phpfpm.pools.roundcube;
localDB = cfg.database.host == "localhost"; localDB = cfg.database.host == "localhost";
user = cfg.database.username; user = cfg.database.username;
phpWithPspell = pkgs.php.withExtensions (e: [ e.pspell ] ++ pkgs.php.enabledExtensions); phpWithPspell = pkgs.php.withExtensions ({ enabled, all }: [ all.pspell ] ++ enabled);
in in
{ {
options.services.roundcube = { options.services.roundcube = {

View File

@ -11,8 +11,8 @@ let
base = pkgs.php74; base = pkgs.php74;
in in
base.buildEnv { base.buildEnv {
extensions = e: with e; extensions = { enabled, all }: with all;
base.enabledExtensions ++ [ enabled ++ [
apcu redis memcached imagick apcu redis memcached imagick
]; ];
extraConfig = phpOptionsStr; extraConfig = phpOptionsStr;

View File

@ -43,8 +43,16 @@ let
phpWithExtensions = self.withExtensions defaultPhpExtensions; phpWithExtensions = self.withExtensions defaultPhpExtensions;
}); });
mkBuildEnv = prevArgs: lib.makeOverridable ( # buildEnv wraps php to provide additional extensions and
{ extensions ? (_: []), extraConfig ? "", ... }@innerArgs: # configuration. Its usage is documented in
# doc/languages-frameworks/php.section.md.
#
# Create a buildEnv with earlier overridden values and
# extensions functions in its closure. This is necessary for
# consecutive calls to buildEnv and overrides to work as
# expected.
mkBuildEnv = prevArgs: prevExtensionFunctions: lib.makeOverridable (
{ extensions ? ({...}: []), extraConfig ? "", ... }@innerArgs:
let let
allArgs = args // prevArgs // innerArgs; allArgs = args // prevArgs // innerArgs;
filteredArgs = builtins.removeAttrs allArgs [ "extensions" "extraConfig" ]; filteredArgs = builtins.removeAttrs allArgs [ "extensions" "extraConfig" ];
@ -54,8 +62,15 @@ let
inherit php phpWithExtensions; inherit php phpWithExtensions;
}); });
allExtensionFunctions = prevExtensionFunctions ++ [ extensions ];
enabledExtensions =
builtins.foldl'
(state: f:
f { enabled = state; all = php-packages.extensions; })
[]
allExtensionFunctions;
getExtName = ext: lib.removePrefix "php-" (builtins.parseDrvName ext.name).name; getExtName = ext: lib.removePrefix "php-" (builtins.parseDrvName ext.name).name;
enabledExtensions = extensions php-packages.extensions;
# Generate extension load configuration snippets from the # Generate extension load configuration snippets from the
# extension parameter. This is an attrset suitable for use # extension parameter. This is an attrset suitable for use
@ -89,9 +104,8 @@ let
inherit (php) version; inherit (php) version;
nativeBuildInputs = [ makeWrapper ]; nativeBuildInputs = [ makeWrapper ];
passthru = { passthru = {
buildEnv = mkBuildEnv allArgs; buildEnv = mkBuildEnv allArgs allExtensionFunctions;
withExtensions = mkWithExtensions allArgs; withExtensions = mkWithExtensions allArgs allExtensionFunctions;
inherit enabledExtensions;
inherit (php-packages) packages extensions; inherit (php-packages) packages extensions;
}; };
paths = [ php ]; paths = [ php ];
@ -108,8 +122,8 @@ let
in in
phpWithExtensions); phpWithExtensions);
mkWithExtensions = prevArgs: extensions: mkWithExtensions = prevArgs: prevExtensionFunctions: extensions:
mkBuildEnv prevArgs { inherit extensions; }; mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; };
pcre' = if (lib.versionAtLeast version "7.3") then pcre2 else pcre; pcre' = if (lib.versionAtLeast version "7.3") then pcre2 else pcre;
in in
@ -218,9 +232,8 @@ let
outputs = [ "out" "dev" ]; outputs = [ "out" "dev" ];
passthru = { passthru = {
enabledExtensions = []; buildEnv = mkBuildEnv {} [];
buildEnv = mkBuildEnv {}; withExtensions = mkWithExtensions {} [];
withExtensions = mkWithExtensions {};
inherit (php-packages) packages extensions; inherit (php-packages) packages extensions;
}; };
@ -258,7 +271,7 @@ let
inherit defaultPhpExtensions; inherit defaultPhpExtensions;
}); });
defaultPhpExtensions = extensions: with extensions; ([ defaultPhpExtensions = { all, ... }: with all; ([
bcmath calendar curl ctype dom exif fileinfo filter ftp gd bcmath calendar curl ctype dom exif fileinfo filter ftp gd
gettext gmp iconv intl json ldap mbstring mysqli mysqlnd opcache gettext gmp iconv intl json ldap mbstring mysqli mysqlnd opcache
openssl pcntl pdo pdo_mysql pdo_odbc pdo_pgsql pdo_sqlite pgsql openssl pcntl pdo pdo_mysql pdo_odbc pdo_pgsql pdo_sqlite pgsql
@ -266,8 +279,8 @@ let
tokenizer xmlreader xmlwriter zip zlib tokenizer xmlreader xmlwriter zip zlib
] ++ lib.optionals (!stdenv.isDarwin) [ imap ]); ] ++ lib.optionals (!stdenv.isDarwin) [ imap ]);
defaultPhpExtensionsWithHash = extensions: defaultPhpExtensionsWithHash = { all, ... }:
(defaultPhpExtensions extensions) ++ [ extensions.hash ]; (defaultPhpExtensions { inherit all; }) ++ [ all.hash ];
php74 = php74base.withExtensions defaultPhpExtensions; php74 = php74base.withExtensions defaultPhpExtensions;
php73 = php73base.withExtensions defaultPhpExtensionsWithHash; php73 = php73base.withExtensions defaultPhpExtensionsWithHash;