There are basically 4 different kinds of php modules, which are packaged for Fedora:
While upstream used 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
(alias pear) : the default channel for PHP Extension and Application Repositorypecl.php.net
(alias pecl) : the default channel for PHP Extension Community Library__uri
: Pseudo-channel for static packagesOther channels must be configured at RPM build time and at at RPM installation time.
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.
Non-PEAR PHP software which provides shared libraries should put its PHP source files for such shared libraries in a subfolder of /usr/share/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 /usr/share/php/Whizz_Bang.
A PSR-0 [^1]
compliant library would put its PHP files in /usr/share/php/
A PSR-4 [^2] compliant library would put its PHP files
in /usr/share/php/
PECL documentation provided by upstream are 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 usefull information about the package and its dependencies.
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).
A CHANNEL package 'MUST have :
Requires: php-pear(PEAR)
Requires(post): %{__pear}
Requires(postun): %{__pear}
Provides: php-channel(channelname)
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}
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 provides 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)
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 has evolved over time and is as follows:
For Fedora (all current versions):
BuildRequires: php-devel
Requires: php(zend-abi) = %{php_zend_api}
Requires: php(api) = %{php_core_api}
PECL extension MUST have ABI check (see previous)
A PECL package MUST also have:
Provides: php-pecl(foo) = %{version}
Provides: php-pecl(foo)%{?_isa} = %{version}
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}
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).
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.
PHP extensions must have a Requires on all of the dependent extensions (php-date, php-gd, php-mbstring, ...). These extensions are virtual Provides of the php sub-packages.
If you need to specify a minimum PHP version, the recommended method is to add a Requires: php(language) >= \$VERSION (where \$VERSION is the minimum PHP version). This works for all released versions of Fedora and RHEL/EPEL-6, but does NOT work for RHEL/EPEL-5. For RHEL/EPEL-5 packages, you will need to use Requires: php-common >= \$VERSION.
Each extension should drop a configuration file in %{php_inidir} and/or %{php_ztsinidir} to enable the extension. This file must contains the name of the loaded extension. Starting with Fedora 21, the file must use a numeric prefix to ensure correct load order:
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 php-devel package in fedora >= 17 (5.4.0) provides the necessary files to build ZTS modules and provides several new 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:
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
The php-pear package provides several useful macros:
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
The php-pear package in Fedora Core 5 and above (version 1:1.4.9-1.2) provides several useful macros:
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)}}
In Fedora 24 and later, 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
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)
The source archive contains a package.xml outside any directory, so you have to use use
%setup -q -c
in your %prep section to avoid writing files to the build root.
To create your initial specfile, you can use the default template provided by the rpmdevtools package:
rpmdev-newspec -t php-pear php-pear-Foo
Or you can generate one; make sure you have the php-pear-PEAR-Command-Packaging package installed:
pear make-rpm-spec Foo.tgz