Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

php.buildEnv: Make the exported php package overridable, improve handling of currently enabled extensions, etc #85026

Merged
merged 11 commits into from Apr 29, 2020

Conversation

talyz
Copy link
Contributor

@talyz talyz commented Apr 11, 2020

Motivation for this change

This does a few different, but related things to the PHP packaging:


It implements the override pattern for builds done with withExtensions / buildEnv, so that we can, for example, write

php.override { fpmSupport = false; }

and get a PHP package with the default extensions enabled, but PHP compiled without fpm support.


It reworks 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

It automatically includes all needed internal dependencies when specifying extensions. Whereas earlier you had to manually include all extensions your wanted extension depended on:

php74.withExtensions ({all, ...}: with all; [ mysqlnd pdo pdo_mysql ])

now specifying the one you want should suffice:

php74.withExtensions ({all, ...}: with all; [ pdo_mysql ])

It deprecates the last config.php.* options, since the affected parameters can now be set directly in the override.


It makes the wrapped php build and the php.ini available as attributes of a package built with php.buildEnv / php.withExtensions.


Note that a large portion of the diff is whitespace changes, so you'll probably want to view it with Hide whitespace changes checked.

cc @aanderse @etu

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

@aanderse
Copy link
Member

aanderse commented Apr 11, 2020

@talyz I appreciate you doing this. My use case for this PR is to override the apacheHttpd argument... any thoughts on that one?

@etu
Copy link
Contributor

etu commented Apr 12, 2020

My use case for this PR is to override the apacheHttpd argument... any thoughts on that one?
Hmm, those arguments have never been passed to the generic function to build a PHP package. So that shouldn't have changed.

My follow up question is: Has that ever worked? It feels like the way to do that is to do something like this:

(pkgs.callPackage <nixos/pkgs/development/interpreters/php> {
  apacheHttpd = pkgs.fooApacheHttpd;
  # ...
}).php74

With this PR I was kinda more expecting it to work with overriding attributes of the generic function such as version and sha256.

For example:

php.override {
  version = "7.3.2";
  sha256 = "0000000000000000000000000000000000000000000000000000";
}

But that doesn't seem to work for me:

error: anonymous function at /home/etu/code/nixpkgs/pkgs/development/interpreters/php/default.nix:4:1 called with unexpected argument 'version', at /home/etu/code/nixpkgs/lib/customisation.nix:133:63

But this to me indicates that @aanderse wish seems to work, because it seems to override the arguments to the php/default.nix file.

I can do for example the following:

php.override { writeText = writeScript; }

It gives me a new output, but it works the same, because writeText and writeScript is fairly similar. To me this indicates that the following should be possible on this branch:

php.override { apacheHttpd = fooDerivation; }

But at the same time

php.override { config.php.fpm = false; }

Seems to work fine, and that's how I thought people usually toggled flags for PHP? But I may be wrong here. And that worked even without this branch.

So to conclude: The only thing I don't know how to easily override is version and sha256 to build another version.

@talyz
Copy link
Contributor Author

talyz commented Apr 12, 2020

Ah, sorry for the confusion. My example did not work, because the callPackages call in all-packages clobbered it. It now works as expected, but doesn't allow overriding arguments to the whole set, which is what @aanderse wants. I'm working on getting that to work too, but that requires some restructuring...

@aanderse
Copy link
Member

It isn't a hard requirement. It's a nice to have. Don't put too much energy into it if it's a difficult task.

php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };

@talyz
Copy link
Contributor Author

talyz commented Apr 12, 2020

No worries! It's tricky, but interesting :) With my latest push you should be able to override both packages and configuration flags, since I've made packages arguments of generic. It does a fair bit of restructuring, but I think it's for the better anyway. With it the following should work:

php.override {
  version = "7.4.3";
  sha256 = "wVF7pJV4+y3MZMc6Ptx21PxQfEp6xjmYFYTMfTtMbRQ=";
  fpmSupport = false;
  valgrind = valgrind-light;
}
(php.withExtensions (e: php.enabledExtensions ++ [ e.imagick ])).override {
  fpmSupport = false;
}

I also reworked the way currently enabled extensions are handled - they're now an argument of the anonymous function fed to withExtensions / buildEnv, which means:

  • You always get the right version of the list of currently enabled extensions
  • It's less verbose, but (hopefully) easier to get right
  • 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, and lastly remove the zip extension like this:

((php.withExtensions (enabled: all: enable ++ [ all.imagick ])).override {
  version = "7.4.3";
  sha256 = "wVF7pJV4+y3MZMc6Ptx21PxQfEp6xjmYFYTMfTtMbRQ=";
  fpmSupport = false;
}).withExtensions (enabled: all: lib.filter (e: e != all.zip) enabled)

Note that a large portion of the diff is whitespace changes, so you'll probably want to view it with Hide whitespace changes checked.

@talyz talyz force-pushed the php_buildenv_override branch 2 times, most recently from bd1dc90 to 055a12a Compare April 15, 2020 14:01
@Izorkin
Copy link
Contributor

Izorkin commented Apr 16, 2020

How to fix this code to enable build php-unit with extensions?

import ../make-test-python.nix ({pkgs, ...}:
 let
    testdir = pkgs.writeTextDir "www/info.php" ''
      <?php
        phpinfo();
      ?>
    '';

in {
  name = "php-fpm-nginx-test";
  meta.maintainers = with pkgs.stdenv.lib.maintainers; [ izorkin ];

  machine = { config, lib, pkgs, ... }: {
    services.unit = {
      enable = true;
      package = pkgs.unit;
      config = ''
        {
          "listeners": {
            "*:9001": {
              "application": "php_73"
            }
          },
          "applications": {
            "php_73": {
              "type": "php 7.3",
              "processes": 1,
              "user": "unit",
              "group": "unit",
              "root": "${testdir}/www",
              "index": "info.php"
            }
          }
        }
      '';
    };
  };
  testScript = ''
    machine.wait_for_unit("unit.service")
    assert "PHP Version ${pkgs.php73.version}" in machine.succeed("curl -vvv -s http://127.0.0.1:9001/")
    assert "opcache" in machine.succeed("curl -vvv -s http://127.0.0.1:9001/")
  '';
})

@talyz
Copy link
Contributor Author

talyz commented Apr 16, 2020

How to fix this code to enable build php-unit with extensions?

Well, the short answer is that I've never used php-unit, so I don't know.

The long answer is that it depends: I'm not sure if it's currently possible in any reasonable fashion - if it simply wraps the php binary, it shouldn't be too problematic, but it seems like it actually embeds PHP somehow, in which case it may require compiling PHP with all wanted extensions. Maybe you can enlighten me as to how php-unit works and how you would normally add extensions to it, and we can see if there are any fixes we can implement to improve the packaging of it.

@Izorkin
Copy link
Contributor

Izorkin commented Apr 17, 2020

I am used this config

################################################################################################################################################################################################################
 # Config Nginx Unit                                                                                                                                                                                            #
 ################################################################################################################################################################################################################

 environment.etc."unit-php72.ini".text = ''
   ; Extensions
   extension = ${pkgs.nur.repos.izorkin.php72Packages-unit.apcu}/lib/php/extensions/apcu.so
   extension = ${pkgs.nur.repos.izorkin.php72Packages-unit.imagick}/lib/php/extensions/imagick.so
   extension = ${pkgs.nur.repos.izorkin.php72Packages-unit.memcached}/lib/php/extensions/memcached.so
   extension = ${pkgs.nur.repos.izorkin.php72Packages-unit.event}/lib/php/extensions/event.so
   extension = ${pkgs.nur.repos.izorkin.php72Packages-unit.protobuf}/lib/php/extensions/protobuf.so
   zend_extension = ${pkgs.nur.repos.izorkin.php72-unit}/lib/php/extensions/opcache.so
 '';

 environment.etc."unit-php73.ini".text = ''
   ; Extensions
   extension = ${pkgs.nur.repos.izorkin.php73Packages-unit.apcu}/lib/php/extensions/apcu.so
   extension = ${pkgs.nur.repos.izorkin.php73Packages-unit.imagick}/lib/php/extensions/imagick.so
   extension = ${pkgs.nur.repos.izorkin.php73Packages-unit.memcached}/lib/php/extensions/memcached.so
   extension = ${pkgs.nur.repos.izorkin.php73Packages-unit.event}/lib/php/extensions/event.so
   extension = ${pkgs.nur.repos.izorkin.php73Packages-unit.protobuf}/lib/php/extensions/protobuf.so
   zend_extension = ${pkgs.nur.repos.izorkin.php73-unit}/lib/php/extensions/opcache.so
 '';

 environment.etc."php.d/default.ini".text = ''
   ; Flags
   allow_url_fopen = off
   allow_url_include = 0ff
   cgi.fix_pathinfo = 0
   fastcgi.logging = on
   error_reporting = E_ALL
   display_errors = off
   display_startup_errors = off
   log_errors = on
   expose_php = Off
   report_memleaks = On
   short_open_tag = On
   zend.enable_gc = On
   ; Limits
   memory_limit = 320M
   post_max_size = 64M
   upload_max_filesize = 64M
   max_execution_time = 30
   max_input_time = 30
   rlimit_files = 131072
   rlimit_core = unlimited
   ; Opcache
   opcache.enable = 1
   opcache.memory_consumption = 128
   opcache.interned_strings_buffer = 8
   opcache.max_accelerated_files = 4000
   opcache.max_file_size = 1M
   opcache.revalidate_freq = 20
   opcache.save_comments = 0
   opcache.load_comments = 0
   opcache.fast_shutdown = 1
   opcache.enable_cli = 0
   opcache.use_cwd = 1
   ; Paths & files
   open_basedir = /dev/urandom:/proc/cpuinfo:/proc/meminfo:/etc/ssl/certs:${cfgPath}
   session.entropy_file = /dev/urandom
   openssl.cafile = /etc/ssl/certs/ca-bundle.crt
   openssl.capath = /etc/ssl/certs
   ; Session
   session.entropy_length = 1024
   session.cookie_httponly = on
   session.hash_function = sha512
   session.hash_bits_per_character = 6
   session.gc_probability = 1
   session.gc_divisor = 1000
   session.gc_maxlifetime = 1440
   ; Session handler
   session.save_handler = memcached
   session.save_path = "127.0.0.1:11211"
   ; Sendmail
   sendmail_path = "/run/wrappers/bin/sendmail -t -i"
   ; Others
   disable_functions = exec,passthru,shell_exec,system,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifcontinued,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig
   emergency_restart_threshold 10
   emergency_restart_interval 1m
   process_control_timeout 10s
   zlib.output_compression = Off
   zlib.output_compression_level = -1
   date.timezone = "Europe/Moscow"
 '';

 systemd.services.unit.serviceConfig.ReadWritePaths = [ "/var/data/mail/pgp" "${cfgHWS02}" ];

 services.unit = {
   enable = true;
   package = pkgs.unit;
   config = ''
     {
       "listeners": {
         "*:8311": {
           "application": "php-72-info"
         },
         "*:8312": {
           "application": "php-72-bench"
         },
         "*:8313": {
           "application": "php-72-prober"
         },
         "*:8321": {
           "application": "php-72-roundcube"
         }
       },
       "applications": {
         "php-72-roundcube": {
           "type": "php 7.2",
           "processes": 4,
           "user": "web-php02",
           "group": "web-php02",
           "root": "${cfgHWS02}/www/public_html",
           "script": "index.php",
           "isolation": {
             "namespaces": {
               "cgroup": true,
               "credential": true,
               "mount": true,
               "pid": true,
               "uname": true
             },
             "uidmap": [
               {"container": 22032, "host": 22032, "size": 1}
             ],
             "gidmap": [
               {"container": 22032, "host": 22032, "size": 1},
               {"container": 32000, "host": 32000, "size": 1},
               {"container": 37101, "host": 37101, "size": 1}
             ]
           },
           "options": {
             "file": "/etc/unit-php72.ini",
             "admin": {
               "max_execution_time": "30",
               "max_input_time": "30",
               "display_errors": "off",
               "display_startup_errors": "off",
               "open_basedir": "/dev/urandom:/etc/ssl/certs:/run/current-system/sw/bin/gpg2:/run/current-system/sw/bin/gpg-agent:/run/current-system/sw/bin/gpgconf:/var/data/mail/pgp:${cfgHWS02}/tmp:${cfgHWS02}/www",
               "sys_temp_dir": "${cfgHWS02}/tmp/misc",
               "upload_tmp_dir": "${cfgHWS02}/tmp/upload",
               "soap.wsdl_cache_dir": "${cfgHWS02}/tmp/wsdl",
             }
           }
         },
         "php-72-info": {
           "type": "php 7.2",
           "processes": 1,
           "user": "web-php01",
           "group": "web-php01",
           "root": "${pkgs.nur.repos.izorkin.php-info}",
           "script": "info.php",
           "isolation": {
             "namespaces": {
               "cgroup": true,
               "credential": true,
               "mount": true,
               "network": true,
               "pid": true,
               "uname": true
             },
             "uidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ],
             "gidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ]
           },
           "options": {
             "file": "/etc/unit-php72.ini",
             "admin": {
               "open_basedir": "/dev/urandom:/etc/ssl/certs:${pkgs.nur.repos.izorkin.php-info}",
             }
           }
         },
         "php-72-bench": {
           "type": "php 7.2",
           "processes": 1,
           "user": "web-php01",
           "group": "web-php01",
           "root": "${pkgs.nur.repos.izorkin.php-bench}",
           "script": "bench.php",
           "isolation": {
             "namespaces": {
               "cgroup": true,
               "credential": true,
               "mount": true,
               "network": true,
               "pid": true,
               "uname": true
             },
             "uidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ],
             "gidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ]
           },
           "options": {
             "file": "/etc/unit-php72.ini",
             "admin": {
               "memory_limit": "256m",
               "max_execution_time": "120",
               "open_basedir": "/dev/urandom:/proc/cpuinfo:/proc/meminfo:/etc/ssl/certs:${pkgs.nur.repos.izorkin.php-bench}",
             }
           }
         },
         "php-72-prober": {
           "type": "php 7.2",
           "processes": 1,
           "user": "web-php01",
           "group": "web-php01",
           "root": "${pkgs.nur.repos.izorkin.php-prober}",
           "script": "prober.php",
           "isolation": {
             "namespaces": {
               "cgroup": true,
               "credential": true,
               "mount": true,
               "pid": true,
               "uname": true
             },
             "uidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ],
             "gidmap": [
               {"container": 22031, "host": 22031, "size": 1}
             ]
           },
           "options": {
             "file": "/etc/unit-php72.ini",
             "admin": {
               "memory_limit": "256m",
               "max_execution_time": "120",
               "open_basedir": "/etc/ssl/certs:/dev/urandom:/proc/cpuinfo:/proc/loadavg:/proc/meminfo:/proc/net/dev:/proc/stat:/proc/uptime:${pkgs.nur.repos.izorkin.php-prober}",
             }
           }
         }
       }
     }
   '';
 };

Modules loading from ini file "options": { "file": "/etc/unit-php72.ini",

@etu
Copy link
Contributor

etu commented Apr 17, 2020

@Izorkin Then it's actually very easy, instead of having the environment.etc."unit-php72.ini".text blocks you can do the following:

...
"options": { "file": "${customPhp}/lib/custom-php.ini" }
...

And make sure to define customPhp with the exensions you need:

let
  customPhp = php.withExtensions (e: php.enabledExtensions ++ [
    e.apcu e.imagick e.memcached e.event e.protobuf 
  ]);
in customPhp

@Izorkin
Copy link
Contributor

Izorkin commented Apr 17, 2020

@etu how to build php-unit with mysqli mysqlnd opcache?

@etu
Copy link
Contributor

etu commented Apr 17, 2020

@Izorkin If it can read from a file to find extensions, you should be able to take the config file I pointed at from the default php attributes (php74, php73, php72) and get that config file. That config file will point to those extensions.

@Izorkin
Copy link
Contributor

Izorkin commented Apr 17, 2020

@etu with this configuration:

{ config, pkgs, ... }:
let
  phpinfo = pkgs.writeTextDir "www/info.php" ''
    <?php
      phpinfo();
    ?>
  '';
  php73-test = pkgs.php73.withExtensions (e: pkgs.php73.enabledExtensions ++ [ e.apcu e.imagick e.memcached e.event e.protobuf]);

in {
  services.unit = {
    enable = true;
    package = pkgs.unit;
    config = ''
      {
        "listeners": {
          "*:8373": {
            "application": "php_73"
          }
        },
        "applications": {
          "php_73": {
            "type": "php 7.3",
            "processes": 1,
            "user": "nginx",
            "group": "nginx",
            "root": "${phpinfo}/www",
            "index": "info.php",
            "options": {
              "file": "${php73-test}/lib/custom-php.ini",
              "admin": {
                "max_execution_time": "30",
                "max_input_time": "30",
                "display_errors": "on",
                "display_startup_errors": "on",
              }
            }
          }
        }
      }
    '';
  };

Error load library

2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/e xtensions/zip.so' (tried: /nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/extensions/zip.so (/nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/ extensions/zip.so: undefined symbol: executor_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-ph p-zip-7.3.16/lib/php/extensions/zip.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib /php/extensions/zip.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib/php/ extensions/zlib.so' (tried: /nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib/php/extensions/zlib.so (/nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib /php/extensions/zlib.so: undefined symbol: sapi_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx- php-zlib-7.3.16/lib/php/extensions/zlib.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.1 6/lib/php/extensions/zlib.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib/php/ extensions/imap.so' (tried: /nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib/php/extensions/imap.so (/nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib /php/extensions/imap.so: undefined symbol: file_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp- php-imap-7.3.16/lib/php/extensions/imap.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.1 6/lib/php/extensions/imap.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

With

...
php73-test = pkgs.php73-unit.withExtensions (e: pkgs.php73-unit.enabledExtensions ++ [ e.apcu e.imagick e.memcached e.event e.protobuf]);
...

Error:

building all machine configurations...
error: php*-unit has been dropped, you can build the same package by using
 something similar with this following snippet:
(php74.override {
  config.php.embed = true;
  config.php.apxs2 = false;
  config.php.systemd = false;
  config.php.phpdbg = false;
  config.php.cgi = false;
  config.php.fpm = false; })

(use '--show-trace' to show detailed location information)

@talyz
Copy link
Contributor Author

talyz commented Apr 17, 2020

@etu with this configuration:

{ config, pkgs, ... }:
let
  phpinfo = pkgs.writeTextDir "www/info.php" ''
    <?php
      phpinfo();
    ?>
  '';
  php73-test = pkgs.php73.withExtensions (e: pkgs.php73.enabledExtensions ++ [ e.apcu e.imagick e.memcached e.event e.protobuf]);

in {
  services.unit = {
    enable = true;
    package = pkgs.unit;
    config = ''
      {
        "listeners": {
          "*:8373": {
            "application": "php_73"
          }
        },
        "applications": {
          "php_73": {
            "type": "php 7.3",
            "processes": 1,
            "user": "nginx",
            "group": "nginx",
            "root": "${phpinfo}/www",
            "index": "info.php",
            "options": {
              "file": "${php73-test}/lib/custom-php.ini",
              "admin": {
                "max_execution_time": "30",
                "max_input_time": "30",
                "display_errors": "on",
                "display_startup_errors": "on",
              }
            }
          }
        }
      }
    '';
  };

Error load library

2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/e xtensions/zip.so' (tried: /nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/extensions/zip.so (/nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib/php/ extensions/zip.so: undefined symbol: executor_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-ph p-zip-7.3.16/lib/php/extensions/zip.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/0npqwzd0xv43rh7lwkma3i5iqyvvsj93-php-zip-7.3.16/lib /php/extensions/zip.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib/php/ extensions/zlib.so' (tried: /nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib/php/extensions/zlib.so (/nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.16/lib /php/extensions/zlib.so: undefined symbol: sapi_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx- php-zlib-7.3.16/lib/php/extensions/zlib.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/5z3v06cdl6yw8j7cy46zpqc1mchl61hx-php-zlib-7.3.1 6/lib/php/extensions/zlib.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
2020/04/17 10:30:32 [notice] 4767#4767 php message: PHP Warning:  PHP Startup: Unable to load dynamic library '/nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib/php/ extensions/imap.so' (tried: /nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib/php/extensions/imap.so (/nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.16/lib /php/extensions/imap.so: undefined symbol: file_globals_id), /nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp- php-imap-7.3.16/lib/php/extensions/imap.so.so (/nix/store/1wm7vgkl1m2hdrr54zd0y6q4i3giwpd7-php-7.3.16/lib/php/extensions//nix/store/9yi0gn1d65lg5qgb5jqhbn58s7i4bvrp-php-imap-7.3.1 6/lib/php/extensions/imap.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

This is close to the right solution, I think. The reason this doesn't work is that you're using extensions built for another PHP. Since unit uses its own PHP build, it needs to expose it somehow in order to allow for overriding and building extensions for it from the services.unit module, which should then implement a .phpExtensions (or similar) which wraps .withExtensions. Also, for this to work properly you probably need the changes in this PR, which allow extensions to be built for the underlying PHP version...

@aanderse
Copy link
Member

@talyz consider the following configuration:

let
  master = import /home/aaron/nixpkgs {}; # a checkout of this branch
in
...
services.httpd = {
  enable = true;
  enablePHP = true;
  adminAddr = "foo@bar.baz";
  phpPackage = master.php.withExtensions (e: with e; [ pdo ]);
  ...
};

I get the this error:

~/nixpkgs> nixos-rebuild build                                                                                                                                                                      
building Nix...
building the system configuration...
error: attempt to call something which is not a function but a list, at /home/aaron/nixpkgs/pkgs/development/interpreters/php/default.nix:62:21
(use '--show-trace' to show detailed location information)

Any ideas?

@talyz
Copy link
Contributor Author

talyz commented Apr 17, 2020

Yes, with my last big update, I changed how php.withExtensions and php.buildEnv handle extensions: they now take a function with two arguments - one which contains all currently enabled extensions and one which contains the set of all extensions.

So

phpPackage = master.php.withExtensions (e: with e; [ pdo ]);

would be written as

phpPackage = master.php.withExtensions (_: e: with e; [ pdo ]);

or, if you want to build one which builds on the previous one

phpPackage =
  master.php.withExtensions
    (enabled: extensions:
      enabled ++ [ extensions.pdo ]);

and, if you later want that PHP build, but want to add pdo_sqlsrv

phpPackage2 =
  phpPackage.withExtensions
    (enabled: extensions:
      enabled ++ [ extensions.pdo_sqlsrv ]);

This means that you always get the right extensions for your version of PHP. It even works if you apply an override which changes the version or build options of PHP in between.

I still have to update the documentation to reflect this, of course.

I realize that it's not very clear from my last update post - sorry about that; I wrote half of it before making this change and the rest after, so yeah.. 😅

@jtojnar
Copy link
Contributor

jtojnar commented Apr 17, 2020

I think I still prefer the single argument function. Having enabled argument does nothing to prevent the issues like the above with unit and it is cleaner and much closer to other language tooling. If we want to avoid the natural pattern of

phpPackage =
  master.php.withExtensions
    (extensions:
      master.php.enabledExtensions ++ [ extensions.pdo ]);

we can just make the php available in the extensions argument:

phpPackage =
  master.php.withExtensions
    (extensions:
      extensions.php.enabledExtensions ++ [ extensions.pdo ]);

It rolls of a tongue slightly worse than python3.pkgs.python that is available in python3.withPackages (pkgs: [...]) but I still find it more convenient.

Or even just make the list of enabled extensions available if we do not want to expose php there:

phpPackage =
  master.php.withExtensions
    (extensions:
      extensions.currentlyEnabled ++ [ extensions.pdo ]);

Copy link
Member

@Ma27 Ma27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, thanks a lot! Just tested it locally and it just works!

Apart from the comments below, I have one question:

Quoting your example in the original post:

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

Why do we modify derivation's attributes here? Isn't this supposed to happen in .overrideAttrs?

nixos/doc/manual/release-notes/rl-2009.xml Outdated Show resolved Hide resolved
pkgs/top-level/aliases.nix Outdated Show resolved Hide resolved
pkgs/development/interpreters/php/default.nix Show resolved Hide resolved
@Izorkin
Copy link
Contributor

Izorkin commented Apr 26, 2020

If build php74.all not found dev package

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
Since all options controlled by the config.php parameters can now be
overridden directly, there's no reason to keep them around.
Some extensions depend on other extensions. Previously, these had to
be added manually to the list of included extensions, or we got a
cryptic error message pointing to strings-with-deps.nix, which wasn't
very helpful. This makes sure all required extensions are included in
the set from which textClosureList chooses its snippets.
@Izorkin
Copy link
Contributor

Izorkin commented Apr 26, 2020

Exension mysqlnd

Compression - not supported

Need zlib.

@Izorkin
Copy link
Contributor

Izorkin commented Apr 26, 2020

Need build php-unit extensions with php-unit package:

+  phpUnitExtensions = php74UnitExtensions;
+  php72UnitExtensions = recurseIntoAttrs php72-unit.extensions;
+  php73UnitExtensions = recurseIntoAttrs php73-unit.extensions;
+  php74UnitExtensions = recurseIntoAttrs php74-unit.extensions;

Package php builded with extensions and ./result/lib/custom-php.ini. Need build php-unit with ./result/lib/custom-php.ini and extensions.

@Izorkin
Copy link
Contributor

Izorkin commented Apr 26, 2020

With this simple changes worked extensions:

diff --git a/pkgs/development/interpreters/php/default.nix b/pkgs/development/interpreters/php/default.nix
index 9b751672fbb..736549abd16 100644
--- a/pkgs/development/interpreters/php/default.nix
+++ b/pkgs/development/interpreters/php/default.nix
@@ -17,18 +17,18 @@ let
     , defaultPhpExtensions

     # Sapi flags
-    , cgiSupport ? true
+    , cgiSupport ? false
     , cliSupport ? true
-    , fpmSupport ? true
+    , fpmSupport ? false
     , pearSupport ? true
     , pharSupport ? true
     , phpdbgSupport ? true

     # Misc flags
-    , apxs2Support ? !stdenv.isDarwin
+    , apxs2Support ? false
     , argon2Support ? true
     , cgotoSupport ? false
-    , embedSupport ? false
+    , embedSupport ? true
     , ipv6Support ? true
     , systemdSupport ? stdenv.isLinux
     , valgrindSupport ? true
@@ -302,6 +302,10 @@ let
   php73 = php73base.withExtensions defaultPhpExtensionsWithHash;
   php72 = php72base.withExtensions defaultPhpExtensionsWithHash;

+  php74-unit = php74base.withExtensions defaultPhpExtensions;
+  php73-unit = php73base.withExtensions defaultPhpExtensionsWithHash;
+  php72-unit = php72base.withExtensions defaultPhpExtensionsWithHash;
+
 in {
-  inherit php72base php73base php74base php72 php73 php74;
+  inherit php72base php73base php74base php72 php73 php74 php72-unit php73-unit php74-unit;
 }
diff --git a/pkgs/servers/http/unit/default.nix b/pkgs/servers/http/unit/default.nix
index f8992bf166b..40873ad991e 100644
--- a/pkgs/servers/http/unit/default.nix
+++ b/pkgs/servers/http/unit/default.nix
@@ -1,8 +1,8 @@
 { stdenv, fetchFromGitHub, which
 , withPython2 ? false, python2
 , withPython3 ? true, python3, ncurses
-, withPHP72 ? false, php72base
-, withPHP73 ? true, php73base
+, withPHP72 ? false, php72-unit, php72base
+, withPHP73 ? true, php73-unit, php73base
 , withPerl528 ? false, perl528
 , withPerl530 ? true, perl530
 , withPerldevel ? false, perldevel
@@ -16,19 +16,7 @@

 with stdenv.lib;

-let
-  phpConfig = {
-    embedSupport = true;
-    apxs2Support = false;
-    systemdSupport = false;
-    phpdbgSupport = false;
-    cgiSupport = false;
-    fpmSupport = false;
-  };
-
-  php72-unit = php72base.override phpConfig;
-  php73-unit = php73base.override phpConfig;
-in stdenv.mkDerivation rec {
+stdenv.mkDerivation rec {
   version = "1.16.0";
   pname = "unit";

@@ -71,8 +59,8 @@ in stdenv.mkDerivation rec {
   postConfigure = ''
     ${optionalString withPython2    "./configure python --module=python2  --config=${python2}/bin/python2-config  --lib-path=${python2}/lib"}
     ${optionalString withPython3    "./configure python --module=python3  --config=${python3}/bin/python3-config  --lib-path=${python3}/lib"}
-    ${optionalString withPHP72      "./configure php    --module=php72    --config=${php72-unit.dev}/bin/php-config    --lib-path=${php72-unit}/lib"}
-    ${optionalString withPHP73      "./configure php    --module=php73    --config=${php73-unit.dev}/bin/php-config    --lib-path=${php73-unit}/lib"}
+    ${optionalString withPHP72      "./configure php    --module=php72    --config=${php72base.dev}/bin/php-config    --lib-path=${php72-unit}/lib"}
+    ${optionalString withPHP73      "./configure php    --module=php73    --config=${php73base.dev}/bin/php-config    --lib-path=${php73-unit}/lib"}
     ${optionalString withPerl528    "./configure perl   --module=perl528  --perl=${perl528}/bin/perl"}
     ${optionalString withPerl530    "./configure perl   --module=perl530  --perl=${perl530}/bin/perl"}
     ${optionalString withPerldevel  "./configure perl   --module=perldev  --perl=${perldevel}/bin/perl"}
diff --git a/pkgs/top-level/aliases.nix b/pkgs/top-level/aliases.nix
index e358c76598d..b5aee14f40a 100644
--- a/pkgs/top-level/aliases.nix
+++ b/pkgs/top-level/aliases.nix
@@ -362,25 +362,6 @@ mapAliases ({
       fpmSupport = false;
     }
   ''; # added 2020-04-01
-  php72-unit = php-unit; # added 2020-04-01
-  php73-unit = php-unit; # added 2020-04-01
-  php74-unit = php-unit; # added 2020-04-01
-
-  phpPackages-unit = throw ''
-    php*Packages-unit has been dropped, you can build something
-     similar with this following snippet:
-    (php74.override {
-      embedSupport = true;
-      apxs2Support = false;
-      systemdSupport = false;
-      phpdbgSupport = false;
-      cgiSupport = false;
-      fpmSupport = false;
-    }).packages
-  ''; # added 2020-04-01
-  php74Packages-unit = phpPackages-unit;
-  php73Packages-unit = phpPackages-unit;
-  php72Packages-unit = phpPackages-unit;

   pidgin-with-plugins = pidgin; # added 2016-06
   pidginlatex = pidgin-latex; # added 2018-01-08
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 4ce001a053f..e6f5b8a6f92 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -9494,9 +9494,14 @@ in
   php73Extensions = recurseIntoAttrs php73.extensions;
   php74Extensions = recurseIntoAttrs php74.extensions;

+  phpUnitExtensions = php74UnitExtensions;
+  php72UnitExtensions = recurseIntoAttrs php72-unit.extensions;
+  php73UnitExtensions = recurseIntoAttrs php73-unit.extensions;
+  php74UnitExtensions = recurseIntoAttrs php74-unit.extensions;
+
   inherit (callPackage ../development/interpreters/php {
     stdenv = if stdenv.cc.isClang then llvmPackages_6.stdenv else stdenv;
-  }) php74 php73 php72 php74base php73base php72base;
+  }) php74 php73 php72 php74base php73base php72base php72-unit php73-unit php74-unit;

   picoc = callPackage ../development/interpreters/picoc {};

@talyz
Copy link
Contributor Author

talyz commented Apr 26, 2020

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

Why do we modify derivation's attributes here? Isn't this supposed to happen in .overrideAttrs?

Because we're actually overriding the call to generic, which takes version and sha256, as arguments, among other things. If you run overrideAttrs on the result, you override the arguments sent to symlinkJoin, which is probably not what we want to do.

@talyz
Copy link
Contributor Author

talyz commented Apr 26, 2020

@Izorkin More extensive changes will have to be made to the unit package and module for this to work well. That's not really within the scope of this PR, though, so we should probably discuss that somewhere else, maybe IRC :)

Copy link
Contributor

@etu etu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very happy with these changes.

I know it breaks earlier use of withExtensions, but I really like the improvements of how it's used so I think it's worth that trade-off.

Unless people have strong opinions against this PR, I plan to merge it by tomorrow night (CEST) around 20:00.

That is about 21 hours from this comment.

I'll also give a go at dropping phpXYbase attributes from being dropped before merge since they aren't actually needed anymore.

@Ma27
Copy link
Member

Ma27 commented Apr 28, 2020

I know it breaks earlier use of withExtensions, but I really like the improvements of how it's used so I think it's worth that trade-off.

Well, the API isn't even on a stable NixOS and the changes and usage are well-discussed and well-documented, so I fully agree here 👍

@talyz thanks a lot!

This is useful if you need to access the dev output of the unwrapped
derivation.
Since the introduction of php.unwrapped there's no real need for the
phpXXbase attributes, so let's remove them to lessen potential
confusion and clutter. Also update the docs to make it clear how to
get hold of an unwrapped PHP if needed.
Instead of using two different php packages in php-packages.nix, one
wrapper and one unwrapped, simply use the wrapper and use its
"unwrapped" attribute when necessary. Also, get rid of the packages
and extensions attributes from the base package, since they're no
longer needed.
@etu etu merged commit 27b9b7b into NixOS:master Apr 29, 2020
@talyz talyz deleted the php_buildenv_override branch April 29, 2020 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants