From Fedora Project Wiki
 
(47 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[TableOfContents(3)] 
= Guidelines for packaging PHP addon modules =


= Proposed Changes to PHP Guidelines =


== Synopsis ==
== Different kinds of packages ==


This page describes items that need to be changed in the [http://fedoraproject.org/wiki/Packaging/PHP PHP Guidelines]  or other items that need to be reviewed or ratified by the [http://fedoraproject.org/wiki/Packaging/Committee Fedora Packaging Comittee]  and [http://fedoraproject.org/wiki/Development/SteeringCommittee FESCo] .
There are basically 4 different kinds of php modules, which are packaged for Fedora:


== PHP Guidelines Changes ==
* [http://pecl.php.net PECL]  (PHP Extention Community Library), which are PHP modules usually written in C, which are dynamically loaded by the PHP interpreter on startup.
 
* [http://pear.php.net PEAR]  (PHP Extension and Application Repository), 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.
=== Packages with Channels ===
* Composer registered libraries,  which are reusable components written in PHP, usually PSR-O compliant classes, registered on  a package registry, most often on [https://packagist.org/ Packagist].
 
* CHANNEL : a package which register a channel. A channel is a repository which provides php extensions
==== Different kinds of packages ====
* Other package providing php extension not handled by pear/pecl mechanisms


To be add
While upstream used the same package and distribution format for PECL and PEAR, creating RPMs has to take some differences into account.
* CHANNEL : a package which register a channel which provides extensions from another repository
* Other package providing php extension not handled by pear/pecl mechanisms


3 channels are defined on installation of php-pear
3 channels are defined on installation of php-pear
Line 22: Line 19:
* <code>__uri</code> : Pseudo-channel for static packages
* <code>__uri</code> : Pseudo-channel for static packages


Other channels must be installed at RPM build time and at at RPM installation time.
Other channels must be configured at RPM build time and at at RPM installation time.


'''FPC notes''' : we already have 4 extras channels in repository (phing, phpdb, symfony and phpunit). A new one (ezc) is on the road. This Guidelines update proposal "try" to avoid change for already approved packages.
{{Anchor|NamingScheme}}


==== Naming scheme ====
== Naming scheme ==


* PECL packages from standard pecl channel should be named php-pecl-PECLPackageName-%{version}-%{release}.%{arch}.rpm.
* PECL packages from standard pecl channel should be named php-pecl-PECLPackageName-%{version}-%{release}.%{arch}.rpm.
Line 32: Line 29:
* CHANNEL packages should be named php-channel-ChannelAlias-%{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.
* 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'' (where "vendor/library" is the known packagist name, "name" attribute in composer.json). When vendor = library, one can be droped (ex symfony/symfony can be named php-symfony).
* Other packages should be named ''php-PackageName-%{version}-%{release}.%{arch}.rpm''; %{arch} can be "noarch" where appropriate.
{{admon/note|above rules for package available in both pear and composer/packagist usually give the same result. Ex Text_Wiki (from pear), as pear/Text_Wiki (packagist), should be named php-pear-text-wiki.}}
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 /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 <ref>[https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md PSR-0]</ref>
compliant library would put its PHP files in /usr/share/php/<Vendor Name>
PEAR documentation provided by upstream are installed in %{pear_docdir}, should stay there, and must be marked as %doc.
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.
== Requires and Provides ==
=== PEAR Packages from the standard channel/repository ===
A PEAR package '''MUST''' have:


<pre>
BuildRequires: php-pear(PEAR)
Requires: php-pear(PEAR)
Requires(post): %{__pear}
Requires(postun): %{__pear}
Provides:    php-pear(foo) = %{version}
</pre>


'''FPC notes''' : PECL/PEAR package from standard channel also follow naming scheme for packages from another channel :)
The virtual provide should match exactly upstream name, including case and underscore, ex: php-pear(Text_Wiki)


Packages which doesn't follow this Guidelines update:
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).
* php-pear-creole => php-phpdb-creole
* php-pear-propel_runtime => php-phpdb-propel-runtime
* php-pear-propel_generator => php-phpdb-propel-generator
* php-pear-phing => php-phing-phing
* php-pear-PHPUnit => php-phpunit-PHPUnit
* php-pear-pake => php-symfony-pake


We could probably accept an exception for this already accepted packages
{{admon/note|This rule allow to keep track of pear dependencies which should slowly disapear.}}


=== CHANNEL Packages ===
=== Packages for CHANNEL (repository) configuration ===


A CHANNEL package ''''MUST''' have :
A CHANNEL package ''''MUST''' have :
Line 56: Line 85:
</pre>
</pre>


=== PEAR Packages from non standard repository ===
=== PEAR Packages from a non standard channel/repository ===


A PEAR package '''MUST''' have:
A PEAR package '''MUST''' have:
Line 70: Line 99:
</pre>
</pre>


=== Composer registered Packages ===
Each package registered on [https://packagist.org/ Packagist] (which is the most widely used registry, so defined as the implicit one) '''MUST''' have
<pre>
Provides:    php-composer(vendor/library) = %{version}
</pre>
Package registered on another registry '''MUST''' have
<pre>
Provides:    php-composer(registry_url/vendor/library) = %{version}
</pre>
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 [https://getcomposer.org/doc/04-schema.md 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 as php-composer(foo)
* require-dev : describes development dependencies, usually useful a build time (ex: to run unit test), so could appears as BuidRequires
* suggest : describes optional dependencies, so could appears as Requires (packager choice)
* conflict : as RPM Conflicts
* replace : as RPM Obsoletes
* provide : for additional virtual provides, must also be in RPM Provides as php-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 has evolved over time and is as follows:
For '''Fedora''' (all current versions):
<pre>
BuildRequires: php-devel
Requires:      php(zend-abi) = %{php_zend_api}
Requires:      php(api) = %{php_core_api}
</pre>
{{admon/important|Packaging note|Details on what to do for EPEL branches EL-4 and EL-5 can be found here: [[Packaging:EPEL#PHP_ABI_Check_Handling]]}}
=== PECL Packages ===
PECL extension '''MUST''' have ABI check (see previous)
A PECL package '''MUST''' also have:
<pre>
BuildRequires: php-pear
Requires(post): %{__pecl}
Requires(postun): %{__pecl}
Provides:    php-pecl(foo) = %{version}
Provides:    php-pecl(foo)%{?_isa}  = %{version}
</pre>
=== PECL Packages from a non standard channel/repository ===
A PECL package from a non standard channel MUST have (instead of previous provides)
<pre>
Requires: php-channel(channelname)
Provides: php-pecl(channelname/foo) = %{version}
Provides: php-pecl(channelname/foo)%{?_isa} = %{version}
</pre>
=== 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.
=== Extensions Requires ===
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.
=== Requiring a Minimum PHP version ===
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.
== C extensions and PECL packages configuration file ==
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:
* 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 ==
== 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 in fedora >= 17 (5.4.0) provides the necessary files to build ZTS modules and provides several new helper macros:
For standard (NTS) extensions
<pre>
%{__php}          %{_bindir}/php
%{php_extdir}    %{_libdir}/php/modules
%{php_inidir}    %{_sysconfdir}/php.d
%{php_incldir    %{_includedir}/php
</pre>


=== CHANNEL package ===
For ZTS extensions
<pre>
%{__ztsphp}      %{_bindir}/zts-php
%{php_ztsextdir}  %{_libdir}/php-zts/modules
%{php_ztsinidir}  %{_sysconfdir}/php-zts.d
%{php_ztsincldir  %{_includedir}/php-zts/php
</pre>


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)


For non default channel extension, pear command requires the channel, so %postun scriplet must specify it
=== Packages for CHANNEL (repository) configuration  ===
 
Here are some recommended scriptlets for properly registering and unregistering the channel:


<pre>
<pre>
Line 85: Line 236:
   %{__pear} channel-update %{pear_xmldir}/%{name}.xml > /dev/null ||:
   %{__pear} channel-update %{pear_xmldir}/%{name}.xml > /dev/null ||:
fi
fi


%postun
%postun
Line 92: Line 242:
fi
fi
</pre>
</pre>
'''FPC notes''' : I recommend to use %{name} for XML file rather than channel or extension name to avoid possible conflicts


=== PEAR Modules ===
=== 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 %{pear_phpdir}, except in Fedora 19+, where it evaluates to /var/lib/pear.)


These defintions for the .spec should be of interest:
These definitions for the .spec should be of interest:
<pre>
<pre>
BuildRequires:    php-pear >= 1:1.4.9-1.2
BuildRequires:    php-pear >= 1:1.4.9-1.2
Line 109: Line 262:
</pre>
</pre>


'''FPC notes''' : i change php to php-common (to avoid httpd dependency)
Be sure you delete any PEAR metadata files at the end of %install:  
<pre>
rm -rf %{buildroot}/%{pear_metadir}/.??*
</pre>


...
{{admon/important|Packaging note|The %{pear_metadir} macro is not present on EPEL, please see: [[EPEL:Packaging#PHP_PEAR_Macros]]}}


And here are some recommended scriptlets for properly registering and unregistering the module, for standard channel:
Here are some recommended scriptlets for properly registering the module:
<pre>
<pre>
%post
%post
%{_bindir}/pear install --nodeps --soft --force --register-only %{pear_xmldir}/%{name}.xml >/dev/null ||:
%{_bindir}/pear install --nodeps --soft --force --register-only %{pear_xmldir}/%{name}.xml >/dev/null ||:
</pre>


And here are some recommended scriptlets for properly unregistering the module, from the standard channel:
<pre>
%postun
%postun
if [ "$1" -eq "0" ] ; then
if [ "$1" -eq "0" ] ; then
Line 124: Line 283:
</pre>
</pre>


For non standard channel;
From a non standard channel (pear command requires the channel):
<pre>
%postun
if [ "$1" -eq "0" ] ; then
%{_bindir}/pear uninstall --nodeps --ignore-errors --register-only Foo_channel/Foo_Bar >/dev/null ||:
fi
</pre>
 
=== PECL Modules ===
 
The php-pear package in Fedora Core 5 and above (version 1:1.4.9-1.2) 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:
<pre>
%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)}}
</pre>
 
And here are some recommended scriptlets for properly registering and unregistering the module:
<pre>
<pre>
%post
%post
%{_bindir}/pear install --nodeps --soft --force --register-only %{pear_xmldir}/%{name}.xml >/dev/null ||:
%{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || :
 


%postun
%postun
if [ "$1" -eq "0" ] ; then
if [ $1 -eq 0 ] ; then
%{_bindir}/pear uninstall --nodeps --ignore-errors --register-only Foo_channel/Foo_Bar >/dev/null ||:
%{pecl_uninstall} %{pecl_name} >/dev/null || :
fi
fi
</pre>
</pre>


{{admon/important|Packaging note|These scriptlets are not correct on EPEL, please see: [[Packaging:EPEL#PHP_PECL_Module_Scriptlets]]}}


'''FPC notes''' : I also recommend to use %{name} for XML file rather than extension name to avoid possible conflicts (we can only use this for non standard channel ank keep using extension name for standard one)
=== 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:
<pre>
%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)
</pre>


== Additional Hints for Packagers ==


=== PEAR & PECL Packages ===


=== Packages with Optional Requires ===
The source archive contains a package.xml outside any directory, so you have to use use
<pre>
%setup -q -c
</pre>
in your %prep section to avoid writing files to the build root.
 
=== PEAR Packages ===
 
To create your initial specfile, you can use the default template provided by the rpmdevtools package:
 
<pre>
rpmdev-newspec -t php-pear php-pear-Foo
</pre>
 
Or you can generate one; make sure you have the php-pear-PEAR-Command-Packaging package installed:
 
<pre>
pear make-rpm-spec Foo.tgz
</pre>


TODO: Some PEAR packages such as [http://pear.php.net/package/Auth Auth]  have many other PEAR packages that are optional Requires.  We need to discuss how to handle splitting up large packages such as this.
== References to the Fedora PHP Packaging Guidelines ==
<references/>


== Other Items ==


No additional items required.
[[Category:Packaging guidelines drafts]]

Latest revision as of 12:10, 2 June 2014

Guidelines for packaging PHP addon modules

Different kinds of packages

There are basically 4 different kinds of php modules, which are packaged for Fedora:

  • PECL (PHP Extention Community Library), which are PHP modules usually written in C, which are dynamically loaded by the PHP interpreter on startup.
  • PEAR (PHP Extension and Application Repository), 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-O compliant classes, registered on a package registry, most often on Packagist.
  • CHANNEL : a package which register a channel. A channel is a repository which provides php extensions
  • Other package providing php extension not handled by pear/pecl mechanisms

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 Repository
  • pecl.php.net (alias pecl) : 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 (where "vendor/library" is the known packagist name, "name" attribute in composer.json). When vendor = library, one can be droped (ex symfony/symfony can be named php-symfony).
  • Other packages should be named php-PackageName-%{version}-%{release}.%{arch}.rpm; %{arch} can be "noarch" where appropriate.
above rules for package available in both pear and composer/packagist usually give the same result. Ex Text_Wiki (from pear), as pear/Text_Wiki (packagist), should be named php-pear-text-wiki.

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 /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/<Vendor Name>

PEAR documentation provided by upstream are installed in %{pear_docdir}, should stay there, and must be marked as %doc.

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.

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).

This rule allow to keep track of pear dependencies which should slowly disapear.

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 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)

  • 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 as php-composer(foo)
  • require-dev : describes development dependencies, usually useful a build time (ex: to run unit test), so could appears as BuidRequires
  • suggest : describes optional dependencies, so could appears as Requires (packager choice)
  • conflict : as RPM Conflicts
  • replace : as RPM Obsoletes
  • provide : for additional virtual provides, must also be in RPM Provides as php-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 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}
Packaging note
Details on what to do for EPEL branches EL-4 and EL-5 can be found here: Packaging:EPEL#PHP_ABI_Check_Handling

PECL Packages

PECL extension MUST have ABI check (see previous)

A PECL package MUST also have:

BuildRequires: php-pear
Requires(post): %{__pecl}
Requires(postun): %{__pecl}

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.

Extensions Requires

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.

Requiring a Minimum PHP version

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.

C extensions and PECL packages configuration file

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:

  • 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 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:

  • 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 %{pear_phpdir}, except in Fedora 19+, where it 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}/.??* 
Packaging note
The %{pear_metadir} macro is not present on EPEL, please see: EPEL:Packaging#PHP_PEAR_Macros

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 in Fedora Core 5 and above (version 1:1.4.9-1.2) 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)}}

And here are some recommended scriptlets for properly registering and unregistering the module:

%post
%{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || :


%postun
if [ $1 -eq 0 ]  ; then
%{pecl_uninstall} %{pecl_name} >/dev/null || :
fi
Packaging note
These scriptlets are not correct on EPEL, please see: Packaging:EPEL#PHP_PECL_Module_Scriptlets

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

PEAR & PECL Packages

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.

PEAR Packages

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

References to the Fedora PHP Packaging Guidelines