PHP Packaging Guidelines
Fedora Packaging Guidelines for PHP addon modules
Different types of PHP packages
There are basically 4 different kinds of PHP modules, which are packaged for Fedora:
-
PECL (PHP Extension Community Library) modules, which are PHP modules usually written in C and are dynamically loaded by the PHP interpreter on startup.
-
PEAR (PHP Extension and Application Repository) modules, which are reusable components written in PHP, usually classes, which can be used in your own PHP applications and scripts by using e.g. the
include()
directive. -
Composer registered libraries, which are reusable components written in PHP, usually PSR-0 compliant classes, registered on a package registry, most often on Packagist.
-
CHANNEL : packages which register a channel. A channel is a repository which provides PHP extensions.
-
Other packages providing a PHP extension not handled by PEAR/PECL mechanisms.
While upstream uses the same package and distribution format for PECL and PEAR, creating RPMs has to take some differences into account.
3 channels are defined on installation of php-pear
:
-
pear.php.net
(aliaspear
) : the default channel for PHP Extension and Application Repository -
pecl.php.net
(aliaspecl
) : the default channel for PHP Extension Community Library -
__uri
: Pseudo-channel for static packages
Other channels must be configured at RPM build time and at at RPM installation time.
Naming scheme
-
PECL packages from standard pecl channel should be named
php-pecl-PECLPackageName-%{version}-%{release}.%{arch}.rpm
. -
PEAR packages from standard pear channel should be named
php-pear-PEARPackageName-%{version}-%{release}.noarch.rpm
. -
CHANNEL packages should be named
php-channel-ChannelAlias-%{version}-%{release}.noarch.rpm
-
Packages from another channel should be named
php-ChannelAlias-PackageName-%{version}-%{release}.noarch.rpm
-
Composer enabled packages (referenced in packagist.org or another registry) should be named
php-vendor-library-%{version}-%{release}.noarch.rpm
(wherevendor/library
is the known packagist name,name
attribute incomposer.json
). Whenvendor
equalslibrary
, one can be dropped (exsymfony/symfony
can be namedphp-symfony
). -
Other packages should be named
php-PackageName-%{version}-%{release}.%{arch}.rpm
;%{arch}
can benoarch
where appropriate.
Please make sure that a pure PHP package (PEAR, packagist…) is correctly being built for noarch
.
As for other packages, name should only use lowercase, underscore and slash replaced by dash.
The PECLPackageName
and the PEARPackageName
should be consistent with the upstream naming scheme. The Crack PHP Extension would thus be named php-pecl-crack
with the resulting packages being php-pecl-crack-0.4-1.i386.rpm
and php-pecl-crack-0.4-1.src.rpm
.
Note that applications that happen to be written in PHP do not belong under the php-*
namespace.
File Placement
Non-PEAR PHP software which provides shared libraries should put its PHP source files for such shared libraries in a subfolder of %{_datadir}/php
, named according to the name of the software. For example, a library called Whizz_Bang (with a RPM called php-something-Whizz-Bang
) would put the PHP source files for its shared libraries in %{_datadir}/php/Whizz_Bang
.
A PSR-0 [1] compliant library would put its PHP files in %{_datadir}/php/
A PSR-4 [2] compliant library would put its PHP files in %{_datadir}/php/
in a PSR-0 compliant tree.
PEAR documentation provided by upstream are installed in %{pear_docdir}
, should stay there, and must be marked as %doc
.
PECL documentation provided by upstream is installed in %{pecl_docdir}
, should stay there, and must be marked as %doc
.
The composer.json
file is not used, and should be installed as %doc
as it provides useful information about the package and its dependencies.
Requires and Provides
PEAR Packages from the standard channel/repository
A PEAR package MUST have:
BuildRequires: php-pear(PEAR) Requires: php-pear(PEAR) Requires(post): %{__pear} Requires(postun): %{__pear} Provides: php-pear(foo) = %{version}
The virtual provide should match exactly upstream name, including case and underscore, ex: php-pear(Text_Wiki)
A PEAR package must have all its dependencies available as PEAR packages, so should only requires those using the php-pear(foo)
virtual provides. Known exception for unbundled libraries (which are often bundled because not available in any PEAR channel).
Packages for CHANNEL (repository) configuration
A CHANNEL package MUST have :
Requires: php-pear(PEAR) Requires(post): %{__pear} Requires(postun): %{__pear} Provides: php-channel(channelname)
PEAR Packages from a non standard channel/repository
A PEAR package MUST have:
BuildRequires: php-channel(channelname) BuildRequires: php-pear(PEAR) Requires: php-pear(PEAR) Requires(post): %{__pear} Requires(postun): %{__pear} Requires: php-channel(channelname) Provides: php-pear(channelname/foo) = %{version}
Composer registered Packages
Each package registered on Packagist (which is the most widely used registry, so defined as the implicit one) MUST have
Provides: php-composer(vendor/library) = %{version}
Package registered on another registry MUST have
Provides: php-composer(registry_url/vendor/library) = %{version}
The virtual provide should match exactly upstream name, including underscore, ex: php-composer(pear/console_table)
Packages moved from PEAR to Composer/Packagist should also Provide php-pear(foo)
when needed (used by other PEAR packages).
Packages must not Require any php-pear(foo)
, but should use php-composer(pear/foo)
.
composer.json
useful attributes (see Composer schema documentation)
-
name
-
description
: 1 line, could be used as RPM summary attribute -
homepage
: could be used as RPM URL attribute -
license
-
require
: describes mandatory dependencies PHP version, PHP extensions or other composer libraries, those must be required by the RPM package asphp-composer(foo)
-
require-dev
: describes development dependencies, usually useful a build time (ex: to run unit test), so could appear as BuidRequires -
suggest
: describes optional dependencies, so could appear as Requires (packager choice) -
conflict
: as RPM Conflicts -
replace
: as RPM Obsoletes -
provide
: for additional virtual provides, must also be in RPM Provides asphp-composer(foo)
C extensions (PECL and others)
To be certain that a binary extension will run correctly with a particular version of PHP, it is necessary to check that a particular package has both API and ABIs matching the installed version of PHP. The mechanism for doing this is as follows:
BuildRequires: php-devel Requires: php(zend-abi) = %{php_zend_api} Requires: php(api) = %{php_core_api}
PECL Packages
PECL extension MUST have ABI check (see C extensions above).
A PECL package MUST also have:
Provides: php-pecl(foo) = %{version} Provides: php-pecl(foo)%{?_isa} = %{version}
PECL Packages from a non standard channel/repository
A PECL package from a non standard channel MUST have (instead of previous provides)
Requires: php-channel(channelname) Provides: php-pecl(channelname/foo) = %{version} Provides: php-pecl(channelname/foo)%{?_isa} = %{version}
Other Packages
PHP addons which are neither PEAR nor PECL should require what makes sense (either a base PHP version or a php-api
, php(zend-abi)
as necessary).
Apache requirement
A PHP library must not have an explicit Requires on php
or httpd
, since these libraries could be used with any webserver or any SAPI (php-cli
, php-cgi
, php-fpm
, …).
Only a PHP web application, which provides a specific Apache httpd configuration, should have a Requires on httpd
and mod_php
.
C extension and PECL package configuration files
Each extension should drop a configuration file in %{php_inidir}
and/or %{php_ztsinidir}
to enable the extension. This file must contain the name of the loaded extension. The file must use a numeric prefix to ensure correct load order:
-
range 00-19 is reserved for zend_extensions (ex:
10-opcache.ini
,15-xdebug.ini
, …) -
range 20-39 is reserved for extensions from php sources (ex:
20-pdo.ini
,30-pdo_pgsql.ini
, …) -
range 40-99 is available for other extensions (ex:
40-zip.ini
, …)
Macros and scriptlets
PHP ZTS extension
When the Apache HTTPD is run in worker mode (instead of prefork mode), the ZTS (Zend Thread Safe) version of PHP is used.
If an extension maintainer wants to provide a ZTS version of this extension, the maintainer must ensure that:
-
the extension is thread safe
-
the libraries used by the extension are thread safe
The php-devel
package provides the necessary files to build ZTS modules and provides several helper macros:
For standard (NTS) extensions
%{__php} %{_bindir}/php %{php_extdir} %{_libdir}/php/modules %{php_inidir} %{_sysconfdir}/php.d %{php_incldir %{_includedir}/php
For ZTS extensions
%{__ztsphp} %{_bindir}/zts-php %{php_ztsextdir} %{_libdir}/php-zts/modules %{php_ztsinidir} %{_sysconfdir}/php-zts.d %{php_ztsincldir %{_includedir}/php-zts/php
php-devel
provides the executables needed during the build of a ZTS extension, which are:
-
zts-phpize
-
zts-php-config
-
zts-php
(which is only useful to run the test suite during build)
Packages for CHANNEL (repository) configuration
Here are some recommended scriptlets for properly registering and unregistering the channel:
%post if [ $1 -eq 1 ] ; then %{__pear} channel-add %{pear_xmldir}/%{name}.xml > /dev/null || : else %{__pear} channel-update %{pear_xmldir}/%{name}.xml > /dev/null ||: fi %postun if [ $1 -eq 0 ] ; then %{__pear} channel-delete %{channelname} > /dev/null || : fi
PEAR Modules
The php-pear
package provides several useful macros:
-
%{pear_phpdir}
-
%{pear_docdir}
(This evaluates to%{_docdir}/pear
.) -
%{pear_testdir}
-
%{pear_datadir}
-
%{pear_xmldir}
-
%{pear_metadir}
(This evaluates to/var/lib/pear
.)
These definitions for the .spec should be of interest:
BuildRequires: php-pear >= 1:1.4.9-1.2 Provides: php-pear(PackageName) = %{version} Requires: php-common >= 4.3, php-pear(PEAR) Requires(post): %{_bindir}/pear Requires(postun): %{_bindir}/pear
Be sure you delete any PEAR metadata files at the end of %install
:
rm -rf %{buildroot}/%{pear_metadir}/.??*
Here are some recommended scriptlets for properly registering the module:
%post %{_bindir}/pear install --nodeps --soft --force --register-only %{pear_xmldir}/%{name}.xml >/dev/null ||:
And here are some recommended scriptlets for properly unregistering the module, from the standard channel:
%postun if [ "$1" -eq "0" ] ; then %{_bindir}/pear uninstall --nodeps --ignore-errors --register-only Foo_Bar >/dev/null ||: fi
From a non standard channel (pear
command requires the channel):
%postun if [ "$1" -eq "0" ] ; then %{_bindir}/pear uninstall --nodeps --ignore-errors --register-only Foo_channel/Foo_Bar >/dev/null ||: fi
PECL Modules
The php-pear
package provides several useful macros:
-
%{pecl_phpdir}
-
%{pecl_docdir}
-
%{pecl_testdir}
-
%{pecl_datadir}
-
%{pecl_xmldir}
You may need to define a few additional macros to extract some information from PHP. It is recommended that you use the following:
%global php_apiver %((echo 0; php -i 2>/dev/null | sed -n 's/^PHP API => //p') | tail -1) %{!?__pecl: %{expand: %%global __pecl %{_bindir}/pecl}} %{!?php_extdir: %{expand: %%global php_extdir %(php-config --extension-dir)}}
Module (un)registration is handled automatically by file triggers in the php-pear
package.
For older releases, here are some recommended scriptlets for properly registering and unregistering a module:
BuildRequires: php-pear Requires(post): %{__pecl} Requires(postun): %{__pecl} %post %{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || : %postun if [ $1 -eq 0 ] ; then %{pecl_uninstall} %{pecl_name} >/dev/null || : fi
Other Modules
If your module includes compiled code, you may need to define some macros to extract some information from PHP. It is recommended that you user the following:
%global php_apiver %((echo 0; php -i 2>/dev/null | sed -n 's/^PHP API => //p') | tail -1) %global php_extdir %(php-config --extension-dir 2>/dev/null || echo "undefined") %global php_version %(php-config --version 2>/dev/null || echo 0)
Additional Hints for Packagers
Want to help? Learn how to contribute to Fedora Docs ›