Connect With Us:
  • Sign Up
  • Login

Articles

post thumb
Imagine a World Without PECL
by Doug/ on 9 Dec 2020

How to Install a PHP Extension in an LfPHP Docker Image Without Using PECL

Starting with PHP 7.4, extending into PHP 8, the (infamous) pecl utility is missing. Although you could recompile PHP with the --with-pear option, the question arises: is there an alternative to pecl? This article addresses how to install PHP extensions in an LfPHP Docker image without using pecl.

What in Heck is LfPHP?

LfPHP stands for Linux for PHP. It's a Docker image based around ASCLinux, a custom-built, high-optimized version of Linux designed specifically for Docker images and cloud service containers. The LfPHP image has tags for PHP versions 5.6 all the way up to 8.0. In addition, it contains Python 2 and 3, perl, Ruby, MariaDB, PostgreSQL, SQLite and even MongoDB. Have a look at the various tags available here: https://hub.docker.com/r/asclinux/linuxforphp-8.2-ultimate. The overall documentation for LfPHP can be found here: https://linux-for-php-documentation.readthedocs.io/en/latest/?badge=latest. Also, have please a look at our other articles on LfPHP on this website.

SHAMELESS PLUG: we also have a cloud service in which you can use the LfPHP image to set up a website.

Got It ... Now What?

As described in the article header, starting with PHP 7.4, the general trend has been to move away from the whole pecl/pear distribution paradigm. The major PHP libraries are available via composer and are visible on packagist.org. But what about extensions written in "C" that need to be compiled? How are we going to do that if pecl isn't available? Before we answer that question we need to first digress and discuss Linux package management.

Linux Package Management

One notable feature of ASCLinux is that all packages are directly compiled, all using the same compiler. This not only ensures fast performance, but also ensures that all packages in the image are compatible. Accordingly, ASCLinux does not use common package management systems such as dkpg, aptitude, yum and so forth. The rationale behind this decision is that existing package management systems do not guarantee compatibility, although, to their credit, they will tell you if there are compatibility issues, and some will even make suggestions and/or attempt to fix the unmet dependencies. Most base Linux Docker images are a hodge-podge of libraries, with a motley assortment of package managers, trying to run in an environment for which they are not suited. ASCLinux avoids this issue by using its own custom package management utility: lfphp-get.

Why Use lfphp-get?

At this point you might be thinking: oh great ... another package manager, right? Before you get too concerned, consider how lfphp-get operates: it uses a simple, extensible scripting mechanism. When you use lfphp-get NAME to install a package NAME, an installer script specific to that package is downloaded from the linuxforphp.net server. Accordingly, when we update a package or library, all we need to do is to update the installer script. There's no need to update the LfPHP Docker image itself, as it uses lfphp-get to build itself. It's also important to note that lfphp-get does check to make sure any dependent libaries/packages are installed.

lfphp-get Basics

lfphp-get is pre-loaded in all LfPHP Docker images in the /bin directory. Enter lfphp-get --self-update to ensure you have the latest version. Like most Linux utilities, enter lfphp-get --help for basic help:

# lfphp-get --help
lfphp-get - a utility to install or compile software packages

lfphp-get [options] package_name

options:
-h, --help                show brief help
--version                 version number of the LFPHP scripts/utilities
--self-update             update all of the LFPHP scripts/utilities
--list                    show list of all available packages
--installed               print a list of installed packages
--compile                 compile the package from source
--force                   force the installation of the package

If you want to be absolutely sure that any given library or utility installed using lfphp-get is 100% pure, simply include the --compile flag. This causes the library or utility to be directly compiled right on the spot. Otherwise, if you don't include this flag, the pre-compiled binaries are installed.

To get a list of packages available, type lfphp-get --list.

# lfphp-get --list
*******************************

* List of available packages: *

*******************************

*aiolib
*alsalib
*aspell
*bind-utils
*blackfire
*chronograf
*cms
*cpio
*cups
*doxygen
*elasticsearch68
*elixir
*enchant-2.0
*enchant-1.6
*erlang
*exakat
*expect
*fcron
*freetds
*giflib
*glib
*grafana
*imagemagick
... not all packages shown here

Now that you've got a basic idea about lfphp-get, before getting back to the original issue about how to install a PHP extension without using pecl, we need to talk about lfphp-get meta packages.

lfphp-get Meta Packages

lfphp-get includes a number of meta packages: packages that can be used to install a further list of packages. One of these is lfphp-get php-frameworks, which, as the option implies, can be used to install a number of PHP frameworks including Laminas, Slim, Laravel and Symfony, among others.

# lfphp-get php-frameworks
--------------------------------------------------------
 1 - Zend Framework Skeleton Application
 2 - Zend Expressive Skeleton Application
 3 - Symfony Application
 4 - Laravel Application
 5 - CakePHP Application
 6 - Slim PHP Skeleton Application
 7 - LightMVC Framework Skeleton Application
 8 - LightMVC Framework Skeleton Application WITH Swoole
 9 - Laminas (Zend Framework 3) Skeleton Application
10 - Mezzio (Zend Expressive) Skeleton Application
--------------------------------------------------------


Which PHP framework application do you wish to install?

(1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10) 

SHAMELESS PLUG: we've created a great training on LfPHP and installing PHP Frameworks. For more information have a look here: https://phptraining.net/courses/lfphp. Anybody who purchases this course gets a one year free cloud services hosting with us!

The meta package to install any PHP extension has this syntax: lfphp-get php-ext EXT-VER, where EXT refers to the short name of the extension, and where VER refers to the version you wish to install. To get the correct combination of EXT-VER, proceed as follows:

  • Go to https://pecl.php.net/packages.php and find the extension you wish to install.
  • EXT is the short name at the top of the page
  • VER is one of the ones listed in the Version column of the Available Releases table

So, in the example shown, the full command would be: lfphp-get php-ext yaml-2.2.0.
NOTE: if you install this extension, you'll also need to install libyaml!

lfphp-get PHP Extension Installation Example

As an example, let's say that you've got a project for a customer who needs to have a bunch of old dBase files converted to MySQL. In this case you'd need to install the PHP dBase extension. You check the listing on pecl.php.net and discover the most recent version that supports PHP 8 is version 7.1.0, Release Candidate 2 released 4 October 2020. Still on the listing page, note that the extension short name is dbase and the targer version (under Available Releases in the Version column) is listed as 7.1.0RC2. Here's a partial dump of the command and output:

root@47074e71847e [ / ]# lfphp-get php-ext dbase-7.1.0RC2
... some lines omitted ...
2020-12-08 04:12:20 (6.98 MB/s) - '/tmp/tmp.7uIIao7iGG' saved [1834/1834]

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 37029  100 37029    0     0  23318      0  0:00:01  0:00:01 --:--:-- 23303
package.xml
dbase-7.1.0RC2/tests/001.phpt
... some lines omitted ...
dbase-7.1.0RC2/dbase.c
... some lines omitted ...
dbase-7.1.0RC2/php_dbase.h
Configuring for:
PHP Api Version:         20190128
Zend Module Api No:      20190128
Zend Extension Api No:   420190128
checking for grep that handles long lines and -e... /bin/grep
... some lines omitted ...
checking whether to build shared libraries... yes
checking whether to build static libraries... no

creating libtool
appending configuration tag "CXX" to libtool
configure: patching config.h.in
configure: creating ./config.status
config.status: creating config.h
/bin/sh /root/tempo/dbase-7.1.0RC2/libtool --mode=compile cc  -I. -I/root/tempo/dbase-7.1.0RC2 -DPHP_ATOM_INC -I/root/tempo/dbase-7.1.0RC2/include -I/root/tempo/dbase-7.1.0RC2/main -I/root/tempo/dbase-7.1.0RC2 -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /root/tempo/dbase-7.1.0RC2/dbf_head.c -o dbf_head.lo
mkdir .libs
 cc -I. -I/root/tempo/dbase-7.1.0RC2 -DPHP_ATOM_INC -I/root/tempo/dbase-7.1.0RC2/include -I/root/tempo/dbase-7.1.0RC2/main -I/root/tempo/dbase-7.1.0RC2 -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /root/tempo/dbase-7.1.0RC2/dbf_head.c  -fPIC -DPIC -o .libs/dbf_head.o
/bin/sh /root/tempo/dbase-7.1.0RC2/libtool --mode=compile cc  -I. -I/root/tempo/dbase-7.1.0RC2 -DPHP_ATOM_INC -I/root/tempo/dbase-7.1.0RC2/include -I/root/tempo/dbase-7.1.0RC2/main -I/root/tempo/dbase-7.1.0RC2 -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /root/tempo/dbase-7.1.0RC2/dbf_rec.c -o dbf_rec.lo
... some lines omitted ...
cp ./.libs/dbase.lai /root/tempo/dbase-7.1.0RC2/modules/dbase.la
PATH="$PATH:/sbin" ldconfig -n /root/tempo/dbase-7.1.0RC2/modules
----------------------------------------------------------------------
Libraries have been installed in:
   /root/tempo/dbase-7.1.0RC2/modules

... some lines omitted ...

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/lib/php/extensions/no-debug-non-zts-20190128/

Done.
Please add this new extension to your php.ini file.

We then need to enable the extension by adding it to the php.ini file:

echo "extension=dbase" >> /etc/php.ini

Testing the Extension

To test the new extension, here's a brief code snippet that creates a database, adds a couple of records, and then retrieves them:

<?php
$fn = '/tmp/test.dbf';
if (file_exists($fn)) unlink($fn);
$def = [
  ['id',   'N', 8, 0],
  ['date', 'D'      ],
  ['name', 'C', 32  ],
  ['age',  'N', 3, 0],
  ['email','C', 128 ],
  ['active', 'L'],
];
if (!$db = dbase_create($fn, $def)) {
  echo "Error, can't create the database\n";
}
$data = [
    [1, date('Ymd'), 'Fred Flintstone', 100, 'fred@bedrock.com', 'T'],
    [2, date('Ymd'), 'Wilma Flintstone', 98, 'wilma@bedrock.com', 'T'],
    [3, date('Ymd'), 'Barney Rubble', 99, 'barney@rubble.com', 'F'],
];
foreach ($data as $entry) {
    dbase_add_record($db, $entry);
}
$patt = '%2d | %8s | %20s | %3d | %20s | %1d' . PHP_EOL;
for ($x = 1; $x <= 3; $x++) {
    $entry = dbase_get_record($db, $x);
    foreach ($entry as $key => $val)
        if (is_string($val)) $entry[$key] = trim($val);
    vprintf($patt, $entry);
}
dbase_close($db);
        

And here is the result of the test:

root@47074e71847e [ /tmp ]# php test.php
 1 | 20201208 |      Fred Flintstone | 100 |     fred@bedrock.com | 1
 2 | 20201208 |     Wilma Flintstone |  98 |    wilma@bedrock.com | 1
 3 | 20201208 |        Barney Rubble |  99 |    barney@rubble.com | 0
root@47074e71847e [ /tmp ]# ls -l
total 8
-rw-r--r-- 1 root root 769 Dec  8 04:54 test.dbf
-rw-r--r-- 1 root root 853 Dec  8 04:53 test.php

Conclusion

One of the key points made in this article is that you need to start thinking about alternatives to pecl. If you are using Docker, especially when deploying to the cloud, consider using the LfPHP docker images as they're custom-built and optimized specifically for use with Docker and a cloud environment. LfPHP includes a great utility called lfphp-get. Over and above its primary function of installing OS packages, this command also includes meta packages that let you install PHP frameworks and extensions. When installing an extension, all you need to know is its short name and the version number you wish to install. Don't forget to add extension=NAME to your php.ini file before use!

So in the words of a famous author (hint: not yours truly!), that's all she wrote. Thanks for reading and happy coding.

Share: