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.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.,
`php74` is PHP 7.4 with commonly used extensions installed,
`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
`php.withExtensions`. This is a function which accepts an anonymous
function as its only argument; the function should take one argument,
the set of all extensions, and return a list of wanted extensions. For
example, a PHP package with the opcache and ImageMagick extensions
enabled:
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 (e: with e; [ imagick opcache ])
php.withExtensions ({ enabled, all }:
enabled ++ [ all.imagick ])
```
Note that this will give you a package with _only_ opcache and
ImageMagick, none of the other extensions which are enabled by default
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:
To exclude some, but not all, of the default extensions, you can
filter the `enabled` list like this:
```nix
php.withExtensions (e:
(lib.filter (e: e != php.extensions.opcache) php.enabledExtensions)
++ [ e.imagick ])
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 simply
ignore `enabled`:
```nix
php.withExtensions ({ all, ... }: with all; [ opcache imagick ])
```
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
php.buildEnv {
extensions = e: with e; [ imagick opcache ];
extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M";
}
```
@ -85,7 +88,7 @@ follows:
```nix
let
myPhp = php.withExtensions (e: with e; [ imagick opcache ]);
myPhp = php.withExtensions ({ all, ... }: with all; [ opcache imagick ]);
in {
services.phpfpm.pools."foo".phpPackage = myPhp;
};
@ -94,7 +97,7 @@ in {
```nix
let
myPhp = php.buildEnv {
extensions = e: with e; [ imagick opcache ];
extensions = { all, ... }: with all; [ imagick opcache ];
extraConfig = "memory_limit=256M";
};
in {
@ -105,8 +108,8 @@ in {
##### Example usage with `nix-shell`
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
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>
<para>
Since this release there's an easy way to customize your PHP install to get a much smaller
base PHP with only wanted extensions enabled. See the following snippet installing a smaller PHP
with the extensions <literal>imagick</literal>, <literal>opcache</literal> and
Since this release there's an easy way to customize your PHP
install to get a much smaller base PHP with only wanted
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:
<programlisting>
environment.systemPackages = [
(pkgs.php.buildEnv { extensions = pp: with pp; [
imagick
opcache
pdo_mysql
]; })
(pkgs.php.withExtensions
({ all, ... }: with all; [
imagick
opcache
pdo
pdo_mysql
])
)
];</programlisting>
The default <literal>php</literal> attribute hasn't lost any extensions -

View file

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

View file

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

View file

@ -43,8 +43,16 @@ let
phpWithExtensions = self.withExtensions defaultPhpExtensions;
});
mkBuildEnv = prevArgs: lib.makeOverridable (
{ extensions ? (_: []), extraConfig ? "", ... }@innerArgs:
# buildEnv wraps php to provide additional extensions and
# 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
allArgs = args // prevArgs // innerArgs;
filteredArgs = builtins.removeAttrs allArgs [ "extensions" "extraConfig" ];
@ -54,8 +62,15 @@ let
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;
enabledExtensions = extensions php-packages.extensions;
# Generate extension load configuration snippets from the
# extension parameter. This is an attrset suitable for use
@ -89,9 +104,8 @@ let
inherit (php) version;
nativeBuildInputs = [ makeWrapper ];
passthru = {
buildEnv = mkBuildEnv allArgs;
withExtensions = mkWithExtensions allArgs;
inherit enabledExtensions;
buildEnv = mkBuildEnv allArgs allExtensionFunctions;
withExtensions = mkWithExtensions allArgs allExtensionFunctions;
inherit (php-packages) packages extensions;
};
paths = [ php ];
@ -108,8 +122,8 @@ let
in
phpWithExtensions);
mkWithExtensions = prevArgs: extensions:
mkBuildEnv prevArgs { inherit extensions; };
mkWithExtensions = prevArgs: prevExtensionFunctions: extensions:
mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; };
pcre' = if (lib.versionAtLeast version "7.3") then pcre2 else pcre;
in
@ -218,9 +232,8 @@ let
outputs = [ "out" "dev" ];
passthru = {
enabledExtensions = [];
buildEnv = mkBuildEnv {};
withExtensions = mkWithExtensions {};
buildEnv = mkBuildEnv {} [];
withExtensions = mkWithExtensions {} [];
inherit (php-packages) packages extensions;
};
@ -258,7 +271,7 @@ let
inherit defaultPhpExtensions;
});
defaultPhpExtensions = extensions: with extensions; ([
defaultPhpExtensions = { all, ... }: with all; ([
bcmath calendar curl ctype dom exif fileinfo filter ftp gd
gettext gmp iconv intl json ldap mbstring mysqli mysqlnd opcache
openssl pcntl pdo pdo_mysql pdo_odbc pdo_pgsql pdo_sqlite pgsql
@ -266,8 +279,8 @@ let
tokenizer xmlreader xmlwriter zip zlib
] ++ lib.optionals (!stdenv.isDarwin) [ imap ]);
defaultPhpExtensionsWithHash = extensions:
(defaultPhpExtensions extensions) ++ [ extensions.hash ];
defaultPhpExtensionsWithHash = { all, ... }:
(defaultPhpExtensions { inherit all; }) ++ [ all.hash ];
php74 = php74base.withExtensions defaultPhpExtensions;
php73 = php73base.withExtensions defaultPhpExtensionsWithHash;