From Fedora Project Wiki

< SIGs‎ | PHP

No edit summary
 
(35 intermediate revisions by 2 users not shown)
Line 42: Line 42:
* "'''require'''" becomes "Requires", using the virtual php-composer(foo)
* "'''require'''" becomes "Requires", using the virtual php-composer(foo)
* "'''require-dev'''" usually becomes "BuildRequires"
* "'''require-dev'''" usually becomes "BuildRequires"
* "'''suggest'''" are optional dependencies, which can becomes (packager choice) "Requires"
* "'''suggest'''" are optional dependencies, which can becomes (packager choice) "Requires", "Recommends" or "Suggests"
* "'''conflict'''" becomes "Conflicts"
* "'''conflict'''" becomes "Conflicts" (be careful, usually not needed)
* "'''replace'''" becomes "Provides" (and "Obsoletes" if needed)
* "'''replace'''" becomes "Provides" (and "Obsoletes" if needed)


Line 70: Line 70:
}
}
</pre>
</pre>
the simple solution is to generate an simple classmap autoloader, using the '''phpab''' command, e.g.
The simplest solution is to generate an simple classmapp autoloader, using the fedora/autoloder, e.g.
<pre>
BuildRequires: php-fedora-autoloader-devel
Requires:      php-composer(fedora/autoloader)
 
%build
%{_bindir}/phpab --template fedora --output src/autoload.php src
</pre>
 
Another deprecated solution is to generate an simple classmap autoloader, only using the '''phpab''' command, e.g.
<pre>
<pre>
BuildRequires: %{_bindir}/phpab
BuildRequires: %{_bindir}/phpab
Line 78: Line 87:
</pre>
</pre>


'''Symfony Framework''': recommended when your package already pull some symfony components and is PSR-0/PSR-4 compliant, e.g.
'''PSR-0/PSR-4''' when "composer.json" describe a library as compliant:


<pre>
<pre>
"require": {
"require": {
   "php": ">=5.3.2",
   "php": ">=5.3.2",
   "symfony/console": "~2.5",
   "symfony/console": "~2.5 || ~3.0",
   "foo/baz": "~1.0"
   "foo/baz": "~1.0"
},
},
Line 90: Line 99:
},
},
</pre>
</pre>
Using the new '''fedora/autoloader''':
<pre>
BuildRequires: php-composer(fedora/autoloader)
Requires:      php-composer(fedora/autoloader)
cat <<'AUTOLOAD' | tee src/Foo/Bar/autoload.php
<?php
require_once '%{_datadir}/php/Fedora/Autoloader/autoload.php';
\Fedora\Autoloader\Autoload::addPsr4('Foo\\Bar\\', __DIR__);
\Fedora\Autoloader\Dependencies::required(array(
    '%{_datadir}/php/Foo/Baz/autoload.php',
    array(
        '%{_datadir}/php/Symfony3/Component/Console/autoload.php', // SF3 preferred
        '%{_datadir}/php/Symfony/Component/Console/autoload.php',  // SF2 as fallback
)));
\Fedora\Autoloader\Dependencies::optional(array(
  // no optional dependencies
));
AUTOLOAD
</pre>
'''Notice''': this solution reduce the dependency tree, avoiding pulling some big framework. It will also avoid some conflicts when multiple version of the Framework exist.
'''Symfony Framework''': alternative solution when your package already pull some symfony components and is PSR-0/PSR-4 compliant, e.g.
In your spec (autoload.php can also be a separate source file):
In your spec (autoload.php can also be a separate source file):
<pre>
<pre>
Line 95: Line 134:
Requires:      php-composer(symfony/class-loader)
Requires:      php-composer(symfony/class-loader)


(cat <<'AUTOLOAD'
cat <<'AUTOLOAD' | tee src/autoload.php
<?php
<?php
if (!isset($fedoraClassLoader) || !($fedoraClassLoader instanceof \Symfony\Component\ClassLoader\ClassLoader)) {
if (!isset($fedoraClassLoader) || !($fedoraClassLoader instanceof \Symfony\Component\ClassLoader\ClassLoader)) {
     if (!class_exists('Symfony\\Component\\ClassLoader\\ClassLoader', false)) {
     if (!class_exists('Symfony\\Component\\ClassLoader\\ClassLoader', false)) {
         require_once '%{_datadir}/php/Symfony/Component/ClassLoader/ClassLoader.php';
         require_once '%{_datadir}/php/Symfony/Component/ClassLoader/ClassLoader.php';
     }
     }
     $fedoraClassLoader = new \Symfony\Component\ClassLoader\ClassLoader();
     $fedoraClassLoader = new \Symfony\Component\ClassLoader\ClassLoader();
     $fedoraClassLoader->register();
     $fedoraClassLoader->register();
}
}
// This library
// This library
$fedoraClassLoader->addPrefix('Foo\\Bar\\', dirname(dirname(__DIR__)));
$fedoraClassLoader->addPrefix('Foo\\Bar\\', dirname(dirname(__DIR__)));
// Another library (dependency)
// Another library (dependency)
require_once '/usr/share/php/Foo/Baz/autoload.php';
require_once '%{_datadir}/php/Foo/Baz/autoload.php';
AUTOLOAD
AUTOLOAD
) | tee src/autoload.php
</pre>
</pre>


'''Notice''': in this implementation proposal, ''$fedoraClassLoader'' can be shared between libraries when various autoloader are stacked.
'''Notice''': in this implementation proposal, ''$fedoraClassLoader'' can be shared between libraries when various autoloader are stacked.


'''Zend Framework''': recommended when your package already pull some ZF components.
'''Zend Framework''': alternative solution when your package already pull some ZF components.


In your spec (autoload.php can also be a separate source file):
In your spec (autoload.php can also be a separate source file):
Line 121: Line 163:
Requires:      php-composer(zendframework/zend-loader)  
Requires:      php-composer(zendframework/zend-loader)  


(cat <<'AUTOLOAD'
cat <<'AUTOLOAD' | tee src/autoload.php
<?php
<?php
require_once ('%{_datadir}/php/Zend/Loader/StandardAutoloader.php');
require_once '%{_datadir}/php/Zend/Loader/AutoloaderFactory.php';
$option = array(
Zend\Loader\AutoloaderFactory::factory(array(
  Zend\Loader\StandardAutoloader::LOAD_NS => array('Foo\\Bar' => __DIR__),
    'Zend\Loader\StandardAutoloader' => array(
  Zend\Loader\StandardAutoloader::AUTOREGISTER_ZF => true,
        'fallback_autoloader' => true, // for other dep, if needed
);
        'autoregister_zf' => true,     // for ZF, if needed
$loader = new Zend\Loader\StandardAutoloader($option);
        'namespaces' => array(
$loader->register();
          'Foo\\Bar' => __DIR__      // Your namespace
))));
AUTOLOAD
AUTOLOAD
) | tee src/autoload.php
</pre>
</pre>
Using AutoloaderFactory ensure a single instance of StandardAutoloader is used.


=== More autoloader tips ===
=== More autoloader tips ===
Line 144: Line 188:


Use '''relative path''' for the library itself, so the autoloader can be included from /usr/share/php or %{buildroot}
Use '''relative path''' for the library itself, so the autoloader can be included from /usr/share/php or %{buildroot}
=== Package versions that introduced autoloaders ===
{{admon/note|Notes|
* Packages that provided an autoloader since their first release will not be listed here''
* This is not an exhaustive list of packages''
}}
{|
! Package
! Autoloader Type
! Autoloader Since Version
|-
|php-Analog
|phpab
|1.0.7
|-
|php-Assetic
|Symfony ClassLoader
|1.2.1-4
|-
|php-aws-sdk
|Symfony ClassLoader
|2.6.15
|-
|php-deepend-Mockery
|Symfony ClassLoader
|0.9.3
|-
|php-digitalsandwich-Phake
|
|
|-
|php-doctrine-annotations
|Symfony ClassLoader
|1.2.6
|-
|php-doctrine-cache
|Symfony ClassLoader
|1.4.1
|-
|php-doctrine-collections
|Symfony ClassLoader
|1.3.0
|-
|php-doctrine-common
|Symfony ClassLoader
|el6: 2.4.3 / !el6: 2.5.0
|-
|php-doctrine-datafixtures
|Symfony ClassLoader
|1.0.2
|-
|php-doctrine-dbal
|Symfony ClassLoader
|el6: 2.4.5 / !el6: 2.5.4
|-
|php-doctrine-Doctrine
|
|
|-
|php-doctrine-inflector
|Symfony ClassLoader
|1.0.1-4
|-
|php-doctrine-lexer
|Symfony ClassLoader
|1.0.1-4
|-
|php-doctrine-orm
|Symfony ClassLoader
|2.4.8
|-
|php-dropbox-php-Dropbox
|
|
|-
|php-drush-drush
|
|
|-
|php-EasyRdf
|Symfony ClassLoader
|0.9.0
|-
|php-egulias-email-validator
|Symfony ClassLoader
|1.2.9
|-
|php-Faker
|Symfony ClassLoader
|1.5.0
|-
|php-gitter
|Symfony ClassLoader
|0.3.0-5
|-
|php-gliph
|Symfony ClassLoader
|0.1.8-4
|-
|php-google-apiclient
|Upstream custom
|1.1.4
|-
|php-goutte
|Symfony ClassLoader
|1.0.7-3
|-
|php-guzzle-Guzzle
|Symfony ClassLoader
|3.9.3
|-
|php-guzzlehttp-guzzle
|Symfony ClassLoader
|5.3.0
|-
|php-guzzlehttp-ringphp
|Symfony ClassLoader
|1.1.0-3
|-
|php-guzzlehttp-streams
|Symfony ClassLoader
|3.0.0-3
|-
|php-ircmaxell-random-lib
|class map
|1.1.0-3
|-
|php-ircmaxell-security-lib
|class map
|1.1.0-4
|-
|php-JMSParser
|Symfony ClassLoader
|1.0.0-7
|-
|php-jsonlint
|Symfony ClassLoader
|1.3.1-3
|-
|php-JsonSchema
|
|
|-
|php-lessphp
|
|
|-
|php-Metadata
|Symfony ClassLoader
|1.5.1-3
|-
|php-mikey179-vfsstream
|phpab
|1.6.0
|-
|php-Monolog
|Symfony ClassLoader
|1.15.0
|-
|php-ocramius-code-generator-utils
|Symfony ClassLoader
|0.3.2-4
|-
|php-ocramius-generated-hydrator
|Symfony ClassLoader
|1.2.0
|-
|php-opencloud
|Symfony ClassLoader
|1.12.2
|-
|php-pclzip
|
|
|-
|php-pear-PhpDocumentor
|
|
|-
|php-phpass
|
|
|-
|php-PHP-Css-Parser
|phpab
|7.0.2
|-
|php-PhpCollection
|Symfony ClassLoader
|0.4.0-4
|-
|php-PhpOption
|Symfony ClassLoader
|1.4.0-4
|-
|php-PHPParser
|
|
|-
|php-pirum-Pirum
|
|
|-
|php-psr-http-message
|phpab
|1.0.0
|-
|php-PsrLog
|Symfony ClassLoader
|1.0.0-8
|-
|php-Raven
|Raven_Autoloader
|0.12.0
|-
|php-react-promise
|Symfony ClassLoader
|2.2.0-4
|-
|php-sabredav-Sabre
|
|
|-
|php-sabredav-Sabre_CalDAV
|
|
|-
|php-sabredav-Sabre_CardDAV
|
|
|-
|php-sabredav-Sabre_DAV
|
|
|-
|php-sabredav-Sabre_DAVACL
|
|
|-
|php-sabredav-Sabre_HTTP
|
|
|-
|php-sabredav-Sabre_VObject
|
|
|-
|php-scssphp
|Symfony ClassLoader
|0.1.6
|-
|php-swift-Swift
|
|
|-
|php-seld-cli-prompt
|Symfony ClassLoader
|1.0.0-3
|-
|php-seld-phar-utils
|Symfony ClassLoader
|1.0.1
|-
|php-solarium
|Symfony ClassLoader
|3.5.0
|-
|php-symfony
|Symfony ClassLoader
|el6: 2.3.31 / !el6: 2.7.1
|-
|php-SymfonyCmfRouting
|Symfony ClassLoader
|1.3.0-4
|-
|php-twig
|Twig_Autoloader
|1.18.2
|-
|php-twig-extensions
|Twig_Extensions_Autoloader
|1.3.0
|-
|php-when
|
|
|-
|php-xmlseclibs
|
|
|-
|php-ZendFramework2
|Zend
|el6: 2.2.10 / !el6: 2.4.7
|}
[[Category:PHP]]
[[Category:PHP]]

Latest revision as of 09:26, 5 December 2019

PHP Packaging Tips

Dependencies

PEAR/PECL

package.xml provided in pear/pecl channel archive describe the dependencies of the package, e.g.

 <dependencies>
  <required>
   <php>
    <min>5.3.0</min>
   </php>
   <package>
    <name>Foo</name>
    <channel>pear.php.net</channel>
    <min>2.0.0</min>
   </package>
  </required>
  <optional>
   <extension>
    <name>curl</name>
   </extension>
  </optional>
 </dependencies>

In your spec

# From package.xml - required
Requires: php(language) >= 5.3.0
Requires: php-pear(Foo) >= 2.0.0
# From package.xml - optional
Requires: php-curl

composer

composer.json provided in lot of libraries/applications describes the dependencies.

See The composer.json schema

  • "require" becomes "Requires", using the virtual php-composer(foo)
  • "require-dev" usually becomes "BuildRequires"
  • "suggest" are optional dependencies, which can becomes (packager choice) "Requires", "Recommends" or "Suggests"
  • "conflict" becomes "Conflicts" (be careful, usually not needed)
  • "replace" becomes "Provides" (and "Obsoletes" if needed)

Autoloader

Explanation

Composer: most library and lot of applications are now "composer" aware. Which means composer is used to install dependencies and create a suitable autoloader. Composer is mostly a "Bundled every library in every project", not something we want in Fedora.

Consumer Autoloader: in some case, we can add an autoloader for an application and all its dependencies. It seems this is not the best solution, if the dependency tree change, the autoloader need to be fixed.

Provider Autoloader: if each library provides an autoloader for its classes and its dependencies, a consumer just have to include this autoloader. This seems the best solution, so recommended here.

Providing an autoloader is not mandatory (so not in the Guidelines), but seems a good practice.

Implementation

There are various way to create an autoloader

upstream: when upstream provides an autoloader, no need to use another one ;)

classmap: when "composer.json" describe a library to autoload a classmap, e.g.

"autoload": {
      "classmap": ["src/"]
}

The simplest solution is to generate an simple classmapp autoloader, using the fedora/autoloder, e.g.

BuildRequires: php-fedora-autoloader-devel
Requires:      php-composer(fedora/autoloader)

%build
%{_bindir}/phpab --template fedora --output src/autoload.php src

Another deprecated solution is to generate an simple classmap autoloader, only using the phpab command, e.g.

BuildRequires: %{_bindir}/phpab

%build
%{_bindir}/phpab --output src/autoload.php src

PSR-0/PSR-4 when "composer.json" describe a library as compliant:

"require": {
   "php": ">=5.3.2",
   "symfony/console": "~2.5 || ~3.0",
   "foo/baz": "~1.0"
},
"autoload": {
    "psr-0": { "Foo\\Bar\\": "src/" }
},

Using the new fedora/autoloader:

BuildRequires: php-composer(fedora/autoloader)
Requires:      php-composer(fedora/autoloader)

cat <<'AUTOLOAD' | tee src/Foo/Bar/autoload.php
<?php
require_once '%{_datadir}/php/Fedora/Autoloader/autoload.php';

\Fedora\Autoloader\Autoload::addPsr4('Foo\\Bar\\', __DIR__);

\Fedora\Autoloader\Dependencies::required(array(
    '%{_datadir}/php/Foo/Baz/autoload.php',
    array(
        '%{_datadir}/php/Symfony3/Component/Console/autoload.php', // SF3 preferred
        '%{_datadir}/php/Symfony/Component/Console/autoload.php',  // SF2 as fallback
)));

\Fedora\Autoloader\Dependencies::optional(array(
   // no optional dependencies
));
AUTOLOAD

Notice: this solution reduce the dependency tree, avoiding pulling some big framework. It will also avoid some conflicts when multiple version of the Framework exist.

Symfony Framework: alternative solution when your package already pull some symfony components and is PSR-0/PSR-4 compliant, e.g.

In your spec (autoload.php can also be a separate source file):

BuildRequires: php-composer(symfony/class-loader)
Requires:      php-composer(symfony/class-loader)

cat <<'AUTOLOAD' | tee src/autoload.php
<?php

if (!isset($fedoraClassLoader) || !($fedoraClassLoader instanceof \Symfony\Component\ClassLoader\ClassLoader)) {
    if (!class_exists('Symfony\\Component\\ClassLoader\\ClassLoader', false)) {
        require_once '%{_datadir}/php/Symfony/Component/ClassLoader/ClassLoader.php';
    }

    $fedoraClassLoader = new \Symfony\Component\ClassLoader\ClassLoader();
    $fedoraClassLoader->register();
}

// This library
$fedoraClassLoader->addPrefix('Foo\\Bar\\', dirname(dirname(__DIR__)));

// Another library (dependency)
require_once '%{_datadir}/php/Foo/Baz/autoload.php';
AUTOLOAD

Notice: in this implementation proposal, $fedoraClassLoader can be shared between libraries when various autoloader are stacked.

Zend Framework: alternative solution when your package already pull some ZF components.

In your spec (autoload.php can also be a separate source file):

BuildRequires: php-composer(zendframework/zend-loader) 
Requires:      php-composer(zendframework/zend-loader) 

cat <<'AUTOLOAD' | tee src/autoload.php
<?php
require_once '%{_datadir}/php/Zend/Loader/AutoloaderFactory.php';
Zend\Loader\AutoloaderFactory::factory(array(
    'Zend\Loader\StandardAutoloader' => array(
        'fallback_autoloader' => true, // for other dep, if needed
        'autoregister_zf' => true,     // for ZF, if needed
        'namespaces' => array(
           'Foo\\Bar' => __DIR__       // Your namespace
))));
AUTOLOAD

Using AutoloaderFactory ensure a single instance of StandardAutoloader is used.

More autoloader tips

Test your autoloader: usually by using it to run the test suite, and better to run the installed copy, e.g.

%check
phpunit --bootstrap=%{buildroot}/%{_datadir}/php/Foo/Bar/autoload.php

Use full path to dependencies autoloader, to ensure you use the expected one (don't rely on include_path to avoid /usr/share/pear which can provide an old version, or SCL which doesn't includes /usr/share/php, or any user altered env.)

Use relative path for the library itself, so the autoloader can be included from /usr/share/php or %{buildroot}

Package versions that introduced autoloaders

Note.png
Notes
  • Packages that provided an autoloader since their first release will not be listed here
  • This is not an exhaustive list of packages


Package Autoloader Type Autoloader Since Version
php-Analog phpab 1.0.7
php-Assetic Symfony ClassLoader 1.2.1-4
php-aws-sdk Symfony ClassLoader 2.6.15
php-deepend-Mockery Symfony ClassLoader 0.9.3
php-digitalsandwich-Phake
php-doctrine-annotations Symfony ClassLoader 1.2.6
php-doctrine-cache Symfony ClassLoader 1.4.1
php-doctrine-collections Symfony ClassLoader 1.3.0
php-doctrine-common Symfony ClassLoader el6: 2.4.3 / !el6: 2.5.0
php-doctrine-datafixtures Symfony ClassLoader 1.0.2
php-doctrine-dbal Symfony ClassLoader el6: 2.4.5 / !el6: 2.5.4
php-doctrine-Doctrine
php-doctrine-inflector Symfony ClassLoader 1.0.1-4
php-doctrine-lexer Symfony ClassLoader 1.0.1-4
php-doctrine-orm Symfony ClassLoader 2.4.8
php-dropbox-php-Dropbox
php-drush-drush
php-EasyRdf Symfony ClassLoader 0.9.0
php-egulias-email-validator Symfony ClassLoader 1.2.9
php-Faker Symfony ClassLoader 1.5.0
php-gitter Symfony ClassLoader 0.3.0-5
php-gliph Symfony ClassLoader 0.1.8-4
php-google-apiclient Upstream custom 1.1.4
php-goutte Symfony ClassLoader 1.0.7-3
php-guzzle-Guzzle Symfony ClassLoader 3.9.3
php-guzzlehttp-guzzle Symfony ClassLoader 5.3.0
php-guzzlehttp-ringphp Symfony ClassLoader 1.1.0-3
php-guzzlehttp-streams Symfony ClassLoader 3.0.0-3
php-ircmaxell-random-lib class map 1.1.0-3
php-ircmaxell-security-lib class map 1.1.0-4
php-JMSParser Symfony ClassLoader 1.0.0-7
php-jsonlint Symfony ClassLoader 1.3.1-3
php-JsonSchema
php-lessphp
php-Metadata Symfony ClassLoader 1.5.1-3
php-mikey179-vfsstream phpab 1.6.0
php-Monolog Symfony ClassLoader 1.15.0
php-ocramius-code-generator-utils Symfony ClassLoader 0.3.2-4
php-ocramius-generated-hydrator Symfony ClassLoader 1.2.0
php-opencloud Symfony ClassLoader 1.12.2
php-pclzip
php-pear-PhpDocumentor
php-phpass
php-PHP-Css-Parser phpab 7.0.2
php-PhpCollection Symfony ClassLoader 0.4.0-4
php-PhpOption Symfony ClassLoader 1.4.0-4
php-PHPParser
php-pirum-Pirum
php-psr-http-message phpab 1.0.0
php-PsrLog Symfony ClassLoader 1.0.0-8
php-Raven Raven_Autoloader 0.12.0
php-react-promise Symfony ClassLoader 2.2.0-4
php-sabredav-Sabre
php-sabredav-Sabre_CalDAV
php-sabredav-Sabre_CardDAV
php-sabredav-Sabre_DAV
php-sabredav-Sabre_DAVACL
php-sabredav-Sabre_HTTP
php-sabredav-Sabre_VObject
php-scssphp Symfony ClassLoader 0.1.6
php-swift-Swift
php-seld-cli-prompt Symfony ClassLoader 1.0.0-3
php-seld-phar-utils Symfony ClassLoader 1.0.1
php-solarium Symfony ClassLoader 3.5.0
php-symfony Symfony ClassLoader el6: 2.3.31 / !el6: 2.7.1
php-SymfonyCmfRouting Symfony ClassLoader 1.3.0-4
php-twig Twig_Autoloader 1.18.2
php-twig-extensions Twig_Extensions_Autoloader 1.3.0
php-when
php-xmlseclibs
php-ZendFramework2 Zend el6: 2.2.10 / !el6: 2.4.7