nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1# We have tests for PCRE and PHP-FPM in nixos/tests/php/ or
2# both in the same attribute named nixosTests.php
3
4let
5 generic =
6 {
7 callPackage,
8 lib,
9 stdenv,
10 nixosTests,
11 tests,
12 fetchurl,
13 makeBinaryWrapper,
14 symlinkJoin,
15 writeText,
16 acl,
17 autoconf,
18 automake,
19 bison,
20 flex,
21 libtool,
22 pkg-config,
23 re2c,
24 apacheHttpd,
25 libargon2,
26 libxml2,
27 pcre2,
28 systemdLibs,
29 system-sendmail,
30 valgrind,
31 xcbuild,
32 writeShellScript,
33 common-updater-scripts,
34 curl,
35 jq,
36 coreutils,
37 formats,
38
39 version,
40 phpSrc ? null,
41 hash ? null,
42 extraPatches ? [ ],
43 packageOverrides ? (final: prev: { }),
44 phpAttrsOverrides ? (final: prev: { }),
45 pearInstallPhar ? (callPackage ./install-pear-nozlib-phar.nix { }),
46
47 # Sapi flags
48 cgiSupport ? true,
49 cliSupport ? true,
50 fpmSupport ? true,
51 pearSupport ? true,
52 pharSupport ? true,
53 phpdbgSupport ? true,
54
55 # Misc flags
56 apxs2Support ? false,
57 argon2Support ? true,
58 cgotoSupport ? false,
59 embedSupport ? false,
60 staticSupport ? false,
61 ipv6Support ? true,
62 zendSignalsSupport ? true,
63 zendMaxExecutionTimersSupport ? false,
64 systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemdLibs,
65 valgrindSupport ?
66 !stdenv.hostPlatform.isDarwin && lib.meta.availableOn stdenv.hostPlatform valgrind,
67 ztsSupport ? apxs2Support,
68 }@args:
69
70 let
71 # buildEnv wraps php to provide additional extensions and
72 # configuration. Its usage is documented in
73 # doc/languages-frameworks/php.section.md.
74 #
75 # Create a buildEnv with earlier overridden values and
76 # extensions functions in its closure. This is necessary for
77 # consecutive calls to buildEnv and overrides to work as
78 # expected.
79 mkBuildEnv =
80 prevArgs: prevExtensionFunctions:
81 lib.makeOverridable (
82 {
83 extensions ? ({ enabled, ... }: enabled),
84 extraConfig ? "",
85 ...
86 }@innerArgs:
87 let
88 allArgs = args // prevArgs // innerArgs;
89 filteredArgs = removeAttrs allArgs [
90 "extensions"
91 "extraConfig"
92 ];
93 php = generic filteredArgs;
94
95 php-packages =
96 (callPackage ../../../top-level/php-packages.nix {
97 phpPackage = phpWithExtensions;
98 }).overrideScope
99 packageOverrides;
100
101 allExtensionFunctions = prevExtensionFunctions ++ [ extensions ];
102 enabledExtensions = builtins.foldl' (
103 enabled: f:
104 f {
105 inherit enabled;
106 all = php-packages.extensions;
107 }
108 ) [ ] allExtensionFunctions;
109
110 getExtName = ext: ext.extensionName;
111
112 # Recursively get a list of all internal dependencies
113 # for a list of extensions.
114 getDepsRecursively =
115 extensions:
116 let
117 deps = lib.concatMap (ext: (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ])) extensions;
118 in
119 if !(deps == [ ]) then deps ++ (getDepsRecursively deps) else deps;
120
121 # Generate extension load configuration snippets from the
122 # extension parameter. This is an attrset suitable for use
123 # with textClosureList, which is used to put the strings in
124 # the right order - if a plugin which is dependent on
125 # another plugin is placed before its dependency, it will
126 # fail to load.
127 extensionTexts = lib.listToAttrs (
128 map (
129 ext:
130 let
131 extName = getExtName ext;
132 phpDeps = (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]);
133 type = "${lib.optionalString (ext.zendExtension or false) "zend_"}extension";
134 in
135 lib.nameValuePair extName {
136 text = "${type}=${ext}/lib/php/extensions/${extName}.so";
137 deps = map getExtName phpDeps;
138 }
139 ) (enabledExtensions ++ (getDepsRecursively enabledExtensions))
140 );
141
142 extNames = map getExtName enabledExtensions;
143 extraInit = writeText "php-extra-init-${version}.ini" ''
144 ${lib.concatStringsSep "\n" (lib.textClosureList extensionTexts extNames)}
145 ${extraConfig}
146 '';
147
148 phpWithExtensions = symlinkJoin {
149 name = "php-with-extensions-${version}";
150 inherit (php) version;
151 nativeBuildInputs = [ makeBinaryWrapper ];
152 passthru = php.passthru // {
153 buildEnv = mkBuildEnv allArgs allExtensionFunctions;
154 withExtensions = mkWithExtensions allArgs allExtensionFunctions;
155 overrideAttrs =
156 f:
157 let
158 phpAttrsOverrides = filteredArgs.phpAttrsOverrides or (final: prev: { });
159 newPhpAttrsOverrides = lib.composeExtensions (lib.toExtension phpAttrsOverrides) (
160 lib.toExtension f
161 );
162 php = generic (filteredArgs // { phpAttrsOverrides = newPhpAttrsOverrides; });
163 in
164 php.buildEnv { inherit extensions extraConfig; };
165 phpIni = "${phpWithExtensions}/lib/php.ini";
166 unwrapped = php;
167 # Select the right php tests for the php version
168 tests = {
169 nixos =
170 lib.recurseIntoAttrs
171 nixosTests."php${lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor php.version)}";
172 package = tests.php;
173 };
174 inherit (php-packages)
175 extensions
176 buildPecl
177 mkComposerRepository
178 mkComposerVendor
179 buildComposerProject
180 buildComposerProject2
181 buildComposerWithPlugin
182 composerHooks
183 composerHooks2
184 mkExtension
185 ;
186 packages = php-packages.tools;
187 meta = php.meta // {
188 outputsToInstall = [ "out" ];
189 };
190 };
191 paths = [ php ];
192 postBuild = ''
193 ln -s ${extraInit} $out/lib/php.ini
194
195 if test -e $out/bin/php; then
196 wrapProgram $out/bin/php --set-default PHP_INI_SCAN_DIR $out/lib
197 fi
198
199 if test -e $out/bin/php-fpm; then
200 wrapProgram $out/bin/php-fpm --set-default PHP_INI_SCAN_DIR $out/lib
201 fi
202
203 if test -e $out/bin/phpdbg; then
204 wrapProgram $out/bin/phpdbg --set-default PHP_INI_SCAN_DIR $out/lib
205 fi
206
207 if test -e $out/bin/php-cgi; then
208 wrapProgram $out/bin/php-cgi --set-default PHP_INI_SCAN_DIR $out/lib
209 fi
210 '';
211 };
212 in
213 phpWithExtensions
214 );
215
216 mkWithExtensions =
217 prevArgs: prevExtensionFunctions: extensions:
218 mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; };
219
220 defaultPhpSrc = fetchurl {
221 url = "https://www.php.net/distributions/php-${version}.tar.bz2";
222 inherit hash;
223 };
224 in
225 stdenv.mkDerivation (
226 finalAttrs:
227 let
228 attrs = {
229 pname = "php";
230
231 inherit version;
232
233 enableParallelBuilding = true;
234
235 nativeBuildInputs = [
236 autoconf
237 automake
238 bison
239 flex
240 libtool
241 pkg-config
242 re2c
243 ]
244 ++ lib.optional stdenv.hostPlatform.isDarwin xcbuild;
245
246 buildInputs =
247 # PCRE extension
248 [ pcre2 ]
249
250 # Enable sapis
251 ++ lib.optionals pearSupport [ libxml2.dev ]
252 ++ lib.optionals (fpmSupport && stdenv.hostPlatform.isLinux) [ acl ]
253
254 # Misc deps
255 ++ lib.optional apxs2Support apacheHttpd
256 ++ lib.optional argon2Support libargon2
257 ++ lib.optional systemdSupport systemdLibs
258 ++ lib.optional valgrindSupport valgrind;
259
260 CXXFLAGS = lib.optionalString stdenv.cc.isClang "-std=c++11";
261 SKIP_PERF_SENSITIVE = 1;
262
263 configureFlags =
264 # Disable all extensions
265 [ "--disable-all" ]
266
267 # PCRE
268 ++ [ "--with-external-pcre=${pcre2.dev}" ]
269
270 # Enable sapis
271 ++ lib.optional (!cgiSupport) "--disable-cgi"
272 ++ lib.optional (!cliSupport) "--disable-cli"
273 ++ lib.optionals fpmSupport [
274 "--enable-fpm"
275 (lib.withFeature stdenv.hostPlatform.isLinux "fpm-acl")
276 ]
277 ++ lib.optionals pearSupport [
278 "--with-pear"
279 "--enable-xml"
280 "--with-libxml"
281 ]
282 ++ lib.optional pharSupport "--enable-phar"
283 ++ lib.optional (!phpdbgSupport) "--disable-phpdbg"
284
285 # Misc flags
286 ++ lib.optional apxs2Support "--with-apxs2=${apacheHttpd.dev}/bin/apxs"
287 ++ lib.optional argon2Support "--with-password-argon2=${libargon2}"
288 ++ lib.optional cgotoSupport "--enable-re2c-cgoto"
289 ++ lib.optional embedSupport "--enable-embed${lib.optionalString staticSupport "=static"}"
290 ++ lib.optional (!ipv6Support) "--disable-ipv6"
291 ++ lib.optional systemdSupport "--with-fpm-systemd"
292 ++ lib.optional valgrindSupport "--with-valgrind=${valgrind.dev}"
293 ++ lib.optional ztsSupport "--enable-zts"
294 ++ lib.optional staticSupport "--enable-static"
295 ++ lib.optional (!zendSignalsSupport) [ "--disable-zend-signals" ]
296 ++ lib.optional zendMaxExecutionTimersSupport "--enable-zend-max-execution-timers"
297
298 # Sendmail
299 ++ [ "PROG_SENDMAIL=${system-sendmail}/bin/sendmail" ];
300
301 hardeningDisable = [ "bindnow" ];
302
303 preConfigure =
304 # Don't record the configure flags since this causes unnecessary
305 # runtime dependencies
306 ''
307 substituteInPlace main/build-defs.h.in \
308 --replace-fail '@CONFIGURE_COMMAND@' '(omitted)'
309 substituteInPlace scripts/php-config.in \
310 --replace-fail '@CONFIGURE_OPTIONS@' "" \
311 --replace-fail '@PHP_LDFLAGS@' ""
312
313 export EXTENSION_DIR=$out/lib/php/extensions
314
315 ./buildconf --copy --force
316
317 if [ -f "scripts/dev/genfiles" ]; then
318 ./scripts/dev/genfiles
319 fi
320 ''
321 + lib.optionalString stdenv.hostPlatform.isDarwin ''
322 substituteInPlace configure --replace-fail "-lstdc++" "-lc++"
323 '';
324
325 # When compiling PHP sources from Github, this file is missing and we
326 # need to install it ourselves.
327 # On the other hand, a distribution includes this file by default.
328 preInstall = ''
329 if [[ ! -f ./pear/install-pear-nozlib.phar ]]; then
330 cp ${pearInstallPhar} ./pear/install-pear-nozlib.phar
331 fi
332 '';
333
334 postInstall = ''
335 test -d $out/etc || mkdir $out/etc
336 cp php.ini-production $out/etc/php.ini
337 '';
338
339 postFixup = ''
340 mkdir -p $dev/bin $dev/lib $dev/share/man/man1
341 mv $out/bin/phpize $out/bin/php-config $dev/bin/
342 mv $out/lib/build $dev/lib/
343 mv $out/share/man/man1/phpize.1.gz \
344 $out/share/man/man1/php-config.1.gz \
345 $dev/share/man/man1/
346
347 substituteInPlace $dev/bin/phpize \
348 --replace-fail "$out/lib" "$dev/lib"
349 ''
350 + lib.optionalString (lib.versionAtLeast version "8.5") ''
351 # PHP 8.5+ has lexbor built into core; dom needs its headers.
352 cp -r ext/lexbor/lexbor $dev/include/php/ext/lexbor/
353 '';
354
355 src = if phpSrc == null then defaultPhpSrc else phpSrc;
356
357 patches =
358 lib.optionals (lib.versionOlder version "8.4") [
359 ./fix-paths-php7.patch
360 ]
361 ++ lib.optionals (lib.versionAtLeast version "8.4") [
362 ./fix-paths-php84.patch
363 ]
364 ++ extraPatches;
365
366 separateDebugInfo = true;
367
368 outputs = [
369 "out"
370 "dev"
371 ];
372
373 passthru = {
374 updateScript =
375 let
376 script = writeShellScript "php${lib.versions.major version}${lib.versions.minor version}-update-script" ''
377 set -o errexit
378 PATH=${
379 lib.makeBinPath [
380 common-updater-scripts
381 curl
382 jq
383 ]
384 }
385 new_version=$(curl --silent "https://www.php.net/releases/active" | jq --raw-output '."${lib.versions.major version}"."${lib.versions.majorMinor version}".version')
386 update-source-version "$UPDATE_NIX_ATTR_PATH.unwrapped" "$new_version" "--file=$1"
387 '';
388 in
389 [
390 script
391 # Passed as an argument so that update.nix can ensure it does not become a store path.
392 (./. + "/${lib.versions.majorMinor version}.nix")
393 ];
394 buildEnv = mkBuildEnv { } [ ];
395 withExtensions = mkWithExtensions { } [ ];
396 overrideAttrs =
397 f:
398 let
399 newPhpAttrsOverrides = lib.composeExtensions (lib.toExtension phpAttrsOverrides) (
400 lib.toExtension f
401 );
402 php = generic (args // { phpAttrsOverrides = newPhpAttrsOverrides; });
403 in
404 php;
405 inherit ztsSupport;
406
407 services.default = {
408 imports = [
409 (lib.modules.importApply ./service.nix {
410 inherit formats coreutils;
411 })
412 ];
413 php-fpm.package = lib.mkDefault finalAttrs.finalPackage;
414 };
415 };
416
417 meta = {
418 description = "HTML-embedded scripting language";
419 homepage = "https://www.php.net/";
420 license = lib.licenses.php301;
421 mainProgram = "php";
422 teams = [ lib.teams.php ];
423 platforms = lib.platforms.all;
424 outputsToInstall = [
425 "out"
426 "dev"
427 ];
428 };
429 };
430 final = attrs // (lib.toExtension phpAttrsOverrides) final attrs;
431 in
432 final
433 );
434in
435generic