Christians Tagebuch: pear

The latest posts in full-text for feed readers.


phorkie is composer-installable

To be able to install all of phorkie's dependencies with composer, I spent a good part of the last week to bring a number of PEAR package onto packagist.org.

Previously I had resorted to composer's PEAR server interface, which is pretty slow and error-prone. Time to get that fixed.

Services_Libravatar

phorkie uses Services_Libravatar to fetch avatar images from e-mail addresses and OpenIDs of logged-in users. It's a library for the libravatar.org API that provides a federated and self-hostable alternative to Gravatar.

The first obstacle was finding the sources, because it had been hosted on gitorious.org, which had been acquired by gitlab and shut down shortly after.

Thanks to the Archiveteam the git repositories have been preserved and I was able to obtain a commit that was missing in my local copy.

After it had been transferred to Github, I configured the travis-ci.org integration and added a proper README.rst file.

OpenID

The OpenID package is used to log people into phorkie without registration and without permanently saving any personal data.

The package had no composer.json file, and when adding it I noticed that three out of five dependencies were also not composer-installable :/

After the dependencies were fixed and the composer.json file added, I tagged it as v0.4.0+composer. That tag is picked up properly by composer as version 0.4.0, and I did not have to increase the version number just for composer compatibility.

Cache_Lite

Cache_Lite is one of the OpenID dependencies. It had a composer.json, but no entry on packagist.org.

Crypt_DiffieHellman

Also required by the OpenID package, Crypt_DiffieHellman had several problems:

  • No composer.json
  • Unit tests did not run anymore
  • Commits from the last release were missing in the git repository

All of them is fixed now.

Services_Yadis

Services_Yadis only needed new dependency versions and a tag.

VersionControl_Git

phorkie uses VersionControl_Git to init, commit into and extract information from git repositories.

When setting up automated unit test runs I saw that some tests were broken. It turned out to be a date issue; the original author had a different timezone and relied on that in the tests. After adjusting the dates and replacing date() calls with gmdate(), the tests run in any timezone now.

Services_Linkback

The PEAR2 Services_Linkback package sends out pingbacks and webmentions, and was missing an entry on packagist.org.

Fin

After all those packages have been made ready, installing dependencies on a phorkie git checkout only requires you to run composer install now. It's time to self-host your pastes and gists! It's never been that easy to get phorkie running :)

Published on 2017-08-29 in ,


PHP7-compatible GeSHi 1.0.9.0 is out

Three years after the last release, on 2017-05-05 Benny and I released version 1.0.9.0 of GeSHi, the Generic Syntax Highlighter for PHP.

The release adds support for PHP 7, brings 14 new languages (256 in all now!) and a bunch of bug fixes. Many thanks to Andreas Gohr for caring about the release and making patches.

GeSHi's source code is in the geshi-1.0 repository on Github and it can be installed via composer from packagist and its own PEAR channel.

Mediawiki switched away

I found out that Mediawiki's SyntaxHighlight_GeSHi dropped GeSHi in 2015 and uses Pygments now:

GeSHi is unmaintained, lacks support for many popular modern languages, and suffers from deep architectural flaws, chief among them the inconsistent tokenization of different languages, each of which requires a custom stylesheet.

Published on 2017-06-27 in ,


PEAR will probably be removed from MacOS X

End of may 2017 I got an e-mail from "Open Source at Apple", stating:

We have some engineers at Apple who were impressed by the Structures_Graph package:

http://pear.php.net/package/Structures_Graph/

The newest version appears to be under an LGPLv3+ license. Would you consider allowing Apple to use this open source code under LGPLv2.1 (or an alternative permissive license) rather than LGPLv3? Thanks!

That "impressed" is probably standard to make people feel important and nudge them towards a oh my, the all-knowing apple corp likes my code! I should give it to them! mindset.

Fact is that Structures_Graph is used in the PEAR installer, which is shipped as part of OSX's PHP packages. Apple simply wanted to continue their current setup without changing anything.

I like the LGPLv3, so I asked:

What exactly is the problem you're facing with the code being LGPLv3+?

Two days later a honest reply:

Some of the concerns center around the LGPLv3+’s express and broad patent grant and its requirement to provide, under certain circumstances, the "installation information” (potentially items like the key used to sign code) for user products that include the Structures_Graph code.

If providing the Structures_Graph code under its former LGPLv2 license is not a viable alternative, is a commercial (paid for) license available?

I had several options now:

  1. Invest some time to change the license back to LGPLv2 to please one of the biggest companies on earth, and get nothing in return.
  2. Request $yearlySalary from Apple for a commercial license and keeping the money to myself, ignoring the people that actually wrote that code years ago.
  3. Tell $bigcorp to either play by the rules or remove the software from the OS.

I chose option three, not only because RMS would have cried otherwise:

I'll keep the license of the package as it is, LGPLv3+. If you cannot accept that I guess then you have to drop the PEAR installer.

I did not get a reply. We'll have to see what the outcome of this will be.

Published on 2017-06-28 in ,


Fixing PHP4 constructors for PHP7

PHP 7 deprecates PHP4-style constructors . In PHP4, class constructor methods had the same name as the class:

]]>

This was bad when switching base classes; you did not only have to change the class' extends declaration, but also calls to the parent constructor. PHP5 then introduced the generic __construct method name for class constructors, which solved the problem.

PHP7 will output a deprecation message when a class with a PHP4-style constructor is loaded (not necessarily used):

PHP Deprecated:  Methods with the same name as their class will not be
constructors in a future version of PHP;
SubClass has a deprecated constructor in /path/to/file.php on line 15

Fixing by renaming

So you're maintaining code - maybe a library - that has such a old-style constructor:

]]>

Note that this code works well, even in PHP7. PHP7 logs the deprecation message, but the code still works.

Users of your library report the deprecation notice, and you decide to bring the code up-to-date with PHP7 by renaming the constructor method:

]]>

You release a new version, and suddenly applications fatally break:

PHP Fatal error:  Call to undefined method SubClass::OldClass()
in /path/to/file.php on line 14

This is because other applications rely on the existence of the old constructor:

OldClass();
    }
}

new SubClass();
?>]]>

A real fix

To not break everyone using your library, you have to keep backwards compatibility: Add the PHP5-style constructor, but keep the PHP4-style one.

]]>

With that change, your code will not emit deprecation messages anymore while keeping compatibility with older applications:

OldClass();
    }
}

class OtherClass extends OldClass
{
    public function __construct()
    {
        parent::__construct();
    }
}

new SubClass();
new OtherClass();
?>]]>

Constructor parameters

You may need to pass constructor parameters, too.

]]>

Notes

PHP Strict standards: Redefining already defined constructor for class

This happens if you put the PHP4 constructor before __construct. The warning will not be raised when __construct is defined first.

Published on 2016-04-10 in ,


Services_Linkback 0.3.0 released

Yesterday I released Services_Linkback in version 0.3.0. It is a PHP library that provides you with everything needed to send and receive webmentions and pingbacks - which are mainly used to notify blogs about new comments on posts.

Services_Linkback is now installable via composer, and all dependencies are also available on packagist now. The HTTP2 package was the last that I had to bring on packagist for that.

Apart from the eased installation, the library got a couple of bug fixes that make it pass all the discovery tests on webmention.rocks! On the single test pages like Discovery Test #2 you will find test comments made via commentpara.de, which uses Services_Linkback for notification sending.

Published on 2016-08-11 in , ,


Importing huge XML files using PHP5 - efficiently and conveniently

At work I had the task to implement the synchronization between an online shop and a commodity management system. Data exchange format was XML - one big XML file for all of the products (some thousands with dozens of attributes). Big question: How do I import the file in a way that is most convenient for me as a programmer - and without exceeding the machine's RAM when loading a 1 GiB file?

I personally prefer SimpleXML for everything XML related in PHP - even to generate XML; although it was never meant to do that primarily. The big problem is that SimpleXML uses DOM in the background which builds the whole XML tree in memory. That's a no-go for large files.

So what's left? Yes, our old and rusty Sax parser. It's not really convenient - you have to catch all this actions for open tags, close tag, data section etc. - but it reads the xml file iteratively. Parsing huge files is no problem if you use Sax. PHP5's slightly enhanced Sax implementation/wrapper is XmlReader which I chose to make use of.

On the other side - my program that synched the data with the database - I wanted to have something dead simple, like a foreach loop. Now the task was to combine XmlReader and SPL's Iterator interface.

Sample XML



 
  Kate
  LGPL
  
Editor Nice KDE text editor
3.5.9 4.0.5
gedit LGPL
Editor Standard gnome text editor
2.22.3 2.22.4-rc1
]]>

Preferred PHP import code

The following code is as easy and beautiful as reading an XML file can get:

Iterator code

Here is the iteration code - without comments - in case you (or /me) need to do the same thing again.

strFile = $strFile;
    }

    public function current() {
        return $this->program;
    }

    public function key() {
        return $this->nKey;
    }

    public function next() {
        $this->program = null;
    }

    public function rewind() {
        $this->reader = new XMLReader();
        $this->reader->open($this->strFile);
        $this->program = null;
        $this->nKey    = null;
    }

    public function valid() {
        if ($this->program === null) {
            $this->loadNext();
        }

        return $this->program !== null;
    }

    /**
     * Loads the next program
     *
     * @return void
     */
    protected function loadNext()
    {
        $strElementName = null;
        $bCaptureValues = false;
        $arValues       = array();
        $arNesting      = array();

        while ($this->reader->read()) {
            switch ($this->reader->nodeType) {
                case XMLReader::ELEMENT:
                    $strElementName = $this->reader->name;
                    if ($bCaptureValues) {
                        if ($this->reader->isEmptyElement) {
                            $arValues[$strElementName] = null;
                        } else {
                            $arNesting[] = $strElementName;
                            $arValues[implode('-', $arNesting)] = null;
                        }
                    }
                    if ($strElementName == $this->strObjectTagname) {
                        $bCaptureValues = true;
                    }
                    break;

                case XMLReader::TEXT:
                    if ($bCaptureValues) {
                        $arValues[implode('-', $arNesting)] = $this->reader->value;
                    }
                    break;

                case XMLReader::CDATA:
                    if ($bCaptureValues) {
                        $arValues[implode('-', $arNesting)] = trim($this->reader->value);
                    }
                    break;

                case XMLReader::END_ELEMENT:
                    if ($this->reader->name == $this->strObjectTagname) {
                        $this->program = $arValues;
                        ++$this->nKey;
                        break 2;
                    }
                    if ($bCaptureValues) {
                        array_pop($arNesting);
                    }
                    break;
            }
        }
    }
}
]]>

There are some things missing, like: namespace and attribute support, handling of tags with the same name in different hierarchy levels, especially the main tag and generally tags that may show up several times. I didn't need it, so do it yourself if it's necessary.

Changelog

2008-08-22
First version
2011-04-27
Support for empty elements
2015-12-13
CDATA support

Published on 2008-08-22 in , ,


PEAR server fully restored

This article has originally been published on the PEAR blog:
PEAR server fully restored @ blog.pear.php.net .

The pear.php.net server has been fully restored after we had to witness a fatal hard drive crash on 2015-11-29.

Our server sponsor eUKhost quickly provided us with a new machine after we told them the old had failed, and the last two weeks were spent setting it up to provide the same functionality as before:

All those things are back again.

Why did it take so long?

The number of active people in the PEAR group has shrunken to about 1.5, with Christian Weiske doing most of the work. I'm also writing this blog post now.

I contacted eUKhost the next morning, and they had a new server available in the evening. Unfortunately it was CentOS and not Debian (which I'm more proficient with), so I asked them if they could put Debian on it. They tried again, but neither Debian nor Ubuntu had the necessary hardware RAID drivers, so we had to go back to CentOS.

Tuesday evening I began to setup the server (note that I have a day job and kids, so I did the resetup in the evenings/at night).

The sources for pear.php.net are PEAR packages themselves , originally written for PHP4. The previous server had PHP 5.3, but now with PHP 5.6 I got serious errors and had to fix the website code at first. And not only the pearweb code itself, but also the dependencies..

Getting into the old code and all the little dependencies and hidden settings (hello, PEAR_BOX constant!) took a big while to get into.

After 8 days, the new server went online with 90% functionality .

Today after 12 days, everything is restored.

Didn't you have a backup?

Jein.

The PEAR package files and the REST XML file structure got synced every 4 hours to my personal server which also acts as PEAR mirror; de.pear.php.net.

Package and website source code is at github; some older packages are still hosted at svn.php.net.

What was not backed up is the website and blog database, and the patch files attached to bugs in the tracker. I had a manual database backup from 2015-03-xx, but nothing regular. Luckily I was able to transfer raw MySQL database files before the disk fully died, so nothing was lost.

Manual and API docs could be regenerated from package files and the peardoc sources, so there is no need to back up the rendered files.

What definitely was not backed up were all the little scripts that e.g. cronjobs called to render the manual, the CHM files and such. They are lost and had to be recreated.

What else went wrong?

The systems@php.net people switched DNS to let pear.php.net point to my mirror server, which went active late in 2015-11-30. Unfortunately my server did not have a SSL certificate for pear.php.net, so people ran into SSL issues when automatically fetching the PEAR installer via HTTPS.

My mirror server did not contain the installer (go-pear.phar and pear-install-nozlib.phar), so downloading via HTTP did also fail until late 2015-11-30.

The PEAR installer itself does not really ready to use mirror servers:

  1. It contacts the main server when you want to set a mirror (#11181)
  2. It downloads the files from the main server because the REST XML files contain full URLs with the main server's domain name (#20995)

So the currently only workable option is to map the original hostname to the mirror IP to be able to continue using the PEAR installer (which we did by updating the DNS entry).

I've seen people complaining that we did not communicate enough, but I think that setting up the new server has higher priority than tweeting. Nevertheless here is the communication timeline:

What now?

I'll setup regular database backups in addition to the package file backups.

The new server has RAID 1, so that the failure of a single disk will not bring down the whole machine.

I don't think that more people get involved in PEAR again; activity has been declining over the last 8 years as composer and github gained traction. Server maintenance thus will continue to be a one-man spare-time show.

Are people still using PEAR?

If you read tweets and forums you'll get that impression that nobody uses PEAR anymore, and everyone is using composer now.

Fact is that there are a lot of old projects using PEAR, and also some new ones - we do have a couple of nice libraries that are documented, unit tested and PHP7 compatible. A lot of the packages are available on packagist.

We had 212.000 downloads in the first three days after the release of the PHP7-compatible PEAR 1.10. And no, this aren't distro package installs - Linux distributions have their own packages.

For me, PEAR packages definitely will stay revelevant.

Published on 2015-12-12 in , ,


Running LDAP tests on travis-ci

After making PEAR compatible with PHP 7, I saw that my ISDN call montior did not start up anymore because of constructor visibility issues in the Net_LDAP2 package.

The original author did not have time to do the fixes anymore, so I stepped up to make the package compatible with PHP 7 and PEAR 1.10.1.

When fixing bugs in a package, I made it a rule to always set up automatic unit test runs when a commit or a git pull request come in; travis-ci makes that particularly easy and has nice integration into github. You see the build status directly in the pull requests and get images to embed into your README to show the current build status.

Net_LDAP2 is a library to interface with LDAP servers, and has a number of tests that require such a server. So getting slapd running on travis-ci was my task, for better test coverage.

LDAP setup

Installation

travis-ci is switching to a docker-based environment, which means that tests start very fast (because no full VMs need to be started up), but also do not allow root access via sudo.

Fortunately for us, the Debian packages slapd (LDAP server) and ldap-utils (Programs to interact with slapd) are on travis' white list, which means that you can install them via a command in your .travis.yml file:

language: php
sudo: false
addons:
  apt:
    packages:
    - ldap-utils
    - slapd
...

Configuration

The LDAP server needs some configuration: The root DN, admin username and password (use slappasswd) as well as some standard schemas like inetorgperson:

# See slapd.conf(5) for details on configuration options.
include   /etc/ldap/schema/core.schema
include   /etc/ldap/schema/cosine.schema
include   /etc/ldap/schema/inetorgperson.schema
include   /etc/ldap/schema/nis.schema

pidfile  /tmp/slapd/slapd.pid
argsfile /tmp/slapd/slapd.args

modulepath /usr/lib/openldap

database  ldif
directory /tmp/slapd

suffix    "dc=example,dc=com"
rootdn    "cn=admin,dc=example,dc=com"
rootpw    {SSHA}AIzygLSXlArhAMzddUriXQxf7UlkqopP

And because we're not runing as root, the server has to run on a port > 1024 . The server has to run in the background, otherwise it would block the following script commands completely.

Apart from the basic configuration, the unit tests require some test data to exist, so we have to import them before. PHP on travis-ci does not have the LDAP module loaded, so we have to take care of this, too.

The resulting travis-ci configuration is the following:

I chose to sleep 3 seconds after starting slapd since one time I had the issue that ldapadd failed because the LDAP server wasn't up.

Results

After all that works now, you can see Net_LDAP2 unit test results on travis-ci, and inspect the full .travis.yml file.

Published on 2015-10-30 in , ,


GitHub merge button uglifies your history

Others already have written lengths about it, but yesterday I saw it again in the code of PEAR's Mail_mime package: An ugly and unreadable git history, caused by simply using the GitHub "Merge pull request" button.

History written by the "Merge pull request" button

GitHub uses --no-ff to create merge commits. Those are unnecessary for most pull requests, especially those with a single commit.

Apart from that, it does not rebase the PR when merging, and it does not squash.

The result is what you see here - simply clicking "merge" on five simple pull requests:


|\  
| * e5be21c Get rid of @-operator, better variables naming 
| * ca1d161 Fix path to pinentry-cli.xml file on composer installs (Bug #20527) 
* |   39ee090 Merge pull request #13 from alecpl/fix_19914 
|\ \  
| * | 51ba6e1 Get rid of @-operator, better variables naming 
| * | 18ecf59 Fix issue where PinEntry could not find Console/CommandLine (Bug #19914) 
| |/  
* | 57ea8c3 adopt package.xml to test file renamings 
* |   7f83425 Merge pull request #12 from alecpl/fix_20512 
|\ \  
| * | c2965cb Fix converting HTML entities in debug output (Bug #20512) 
| |/  
* |   557ab17 Merge pull request #14 from alecpl/fix_17815 
|\ \  
| * \   bb42e1e Merge branch 'fix_17815' of github.com:alecpl/Crypt_GPG into fix_17815 
| |\ \  
| | * | 3abdafb Fix key import tests after adding 'fingerprints' item to the result 
| * | | 57ccba0 Fix key import tests after adding 'fingerprints' item to the result 
| |/ /  
| * | a6cbcd0 Return all fingerprintes from key import methods (Request #17815) 
| |/  
* |   e5ea0df Merge pull request #15 from alecpl/fix_typos 
|\ \  
| |/  
|/|   
| * 33e3c94 Fix typos in variable names 
|/  
* c340099 make tests run on travis-ci.org ]]>

Pretty history

Compare this with the history of my JsonMapper repository, in which I always manually merge pull requests after rebasing and squashing them:


* c27b7c4 Fix namespace error with setter type hints 
* d2ed3f3 (tag: v0.6.0) Update changelog; release 0.6.0 
* 5da7642 Add keywords to readme 
* f035a40 Prefer setter methods over directy property access 
* 6059b72 Change setter method name calculation for properties with _ underscores 
* 11bceb0 Issue #28: update README; test for underscores 
* 6d1de35 (tag: v0.5.0) Run tests on php7, too 
* 5d7befc Update changelog, prepare release of 0.5.0 
* af33d36 Check for "double" instead of "float" 
* 00a7b68 Fix CS 
* c830437 Support nullable types (int|null) 
* 7a294e3 Increase test coverage to 100% 
]]>

Other rants about the merge button

Published on 2015-08-19 in , , ,


PEAR 1.10.0dev1 brings PHP 7 compatibility!

The new PEAR installer release adds PHP 7 support while dropping support for PHP 4 - 5.3. It also fixes a nasty SSL issue that made it hard to use on PHP 5.6. With the update, strict warnings about static calls to a non-static PEAR::isError() are a thing of the past.

I've just published the first preview version: PEAR 1.10.0dev1.

You can upgrade your existing PEAR version with the following command:

$ pear upgrade PEAR-1.10.0dev1

Pre-release versions of go-pear.phar and install-pear-nozlib.phar can be temporarily be found at

Please report any bugs you find on the PEAR bug tracker or on the pear-dev mailing list.

Published on 2015-07-25 in ,