Creating a simple link registry – Matthias Noback

The problem: if you publish any document as PDF, in print, etc. and the text contains URLs, there is a chance that one day those URLs won’t work anymore.
There’s nothing to do about that, it happens.

Luckily, this is a solved problem.
The solution is to link to a stable and trustworthy website, that is, one that you maintain and host (of course, you’re trustworthy!).
Then in the document you link to that website, and the website redirects visitors to the actual location.

An example: my book contains a link to https://enjoy.gitstore.app/repositories/matthiasnoback/read-with-the-author.
When I moved that repository to a new organization on GitHub, this link resulted in a 404 Page not found error.
The proper URL is now https://enjoy.gitstore.app/repositories/read-with-the-author/read-with-the-author.
Chris from Gitstore was able to save the day by setting up a redirect on their site, but I wanted to make sure this kind of problem would never be a problem for me again.

The ingredients for the solution:

  • A domain name (I registered advwebapparch.com)
  • A simple website that can redirect visitors to the actual locations

I wanted to hook this new website into my existing Docker-based setup which uses Traefik to forward traffic to the right container based on labels.
It turns out, with a simple Nginx image and some custom setup we can easily set up a website that is able to redirecting visitors.

The Dockerfile for such an image:

FROM nginx:stable-alpine
COPY default.conf /etc/nginx/conf.d/default.conf

Where default.conf looks like this:

server { listen 80 default_server; index index.html; root /srv; error_page 404 /404.html; rewrite /repository https://enjoy.gitstore.app/repositories/read-with-the-author/read-with-the-author redirect;
}

This already works, and when I deploying the resulting image to the server that receives traffic for advwebapparch.com, a request for /repository will indeed redirect a visitor to https://enjoy.gitstore.app/repositories/read-with-the-author/read-with-the-author using a temporary redirect.

Generating the Nginx configuration from a text file

When I’m working on my book, I don’t want to manually update a server configuration file every time I’m adding a URL.
Instead, I’d like to work with a simple text file.
Let’s name this file forwards.txt:

/repository https://enjoy.gitstore.app/repositories/read-with-the-author/read-with-the-author
/blog https://matthiasnoback.nl

And then I want the Docker image build process to add rewrite rules automatically,
So I wrote a little PHP script that does this runs during the build.
Here’s what the Dockerfile looks like.
It uses a multi-stage build:

FROM php:7.4-alpine as php
# This will copy build.php from the build context to the image
COPY . .
# This will generate default.conf based on template.conf
RUN php build.php FROM nginx:stable-alpine
# Copy the default.conf from the php image to the nginx image
COPY --from=php default.conf /etc/nginx/conf.d/default.conf

Here’s what happens inside the PHP script:

function insertRewritesInNginxConf(string $conf): string
{ $rewrites = []; foreach (file('forwards.txt') as $line) { $line = trim($line); if (empty($line)) { continue; } $rewrites[] = ' ' . 'rewrite ' . $line . ' redirect;'; } return str_replace( '%INSERT_URL_REWRITES_HERE%', implode("\n", $rewrites), $conf );
} /* * Generate the Nginx configuration which includes all the actual * redirect instructions */
file_put_contents( 'default.conf', insertRewritesInNginxConf(file_get_contents('template.conf'))
);

We should add a bit of validation for the data from the forwards.txt file so we don’t end up with a broken Nginx configuration, but otherwise, this works just fine.

I don’t want to manually check that all the links that are inside the “link registry” still work.
Instead, I’d like to use Oh Dear for that, which does uptime monitoring and checks for broken links as well.

For this purpose I added another function to the PHP script, which, based

Truncated by Planet PHP, read more at the original (another 1844 bytes)

Making bugs ex-bugs with Xdebug – platform.sh

Xdebug is an indispensable tool for every PHP developer. PHP’s favorite real-time debugger, it supports breakpoints, more detailed debug output, and deeper introspection of PHP code to determine just what it’s doing (and what it’s doing wrong). Sadly, it comes at a huge cost in performance, though, making it unsuitable for production.
Not on Platform.sh, though. Xdebug is now available on all Grid environments, secure and without a performance loss.

Switching phubb’s HTTP client – Christian Weiske

phubb is a WebSub hub that notifies subscribers in realtime when your website is updated.

Up to this year, phubb sent HTTP requests (GET + POST) with file_get_contents() and a HTTP stream context – see my previous example.

But then I needed a 100% correct way of detecting a page’s Hub URL, and copied the code from phinde, my blog search engine. With that I introduced a dependency to PEAR’s good old HTTP_Request2 library and I decided to use that library for all requests.

Unfortunately, now the problems began: During development I got an error in about one of 10-20 requests on my machine and could not find the cause:

PHP Fatal error: Uncaught HTTP_Request2_MessageException: Malformed response: in HTTP/Request2/Adapter/Socket.php on line 1019 #0 HTTP/Request2/Adapter/Socket.php(1019): HTTP_Request2_Response->__construct('', true, Object(Net_URL2))
#1 HTTP/Request2/Adapter/Socket.php(136): HTTP_Request2_Adapter_Socket->readResponse()
#2 HTTP/Request2.php(946): HTTP_Request2_Adapter_Socket->sendRequest(Object(phubb\HttpRequest))
#3 phubb/src/phubb/HttpRequest.php(22): HTTP_Request2->send()
#4 phubb/src/phubb/Task/Publish.php(283): phubb\HttpRequest->send()
#5 phubb/src/phubb/Task/Publish.php(248): phubb\Task_Publish->fetchTopic(Object(phubb\Model_Topic))
#6 phubb/src/phubb/Task/Publish.php(77): phubb\Task_Publish->checkTopicUpdate('http://push-tes...')
#7 in HTTP/Request2/Response.php on line 215

The socket adapter has this problem, and I did not want to try to debug that strange problem. (No idea if the cURL one has it; I do not want to rely on php-curl). Finding a new HTTP library was the only option.

New HTTP library

The PHP Framework Interop Group has several HTTP-related proposals; one of them PSR-18: HTTP Client. Now that we have a standardized way to send HTTP requests in 2020, I should use a library that implements it.

The psr-18 topic on Github listed some clients:

Symfony’s HTTP client was among them, and it provides a mock client for unit tests! Unfortunately, it also introduces a million dependencies.

There were two others that looked ok-ish on first sight (diciotto and http-client-curl) but both of them had no mock client, and the latter was even curl only. Again nothing for me.

Then I found PHP-HTTP that promises a standard interface for HTTP clients in PHP, and it supports PSR-18! It even has a socket client that has nearly no dependencies, and a mock client for unit tests. I’ll try that one for now.

Platform.sh + Lando: local dev in perfect sync with the cloud – platform.sh

Platform.sh removes a major pain point for developers: having to invest time in managing servers, virtual machines, or containers. Instead, Platform.sh enables developers to focus 100% of their time on their code. Since the beginning, Platform.sh has provided instant cloning capability, so dev teams can work on perfect copies of their production sites in the cloud for every Git branch.
Now, in partnership with Lando, we’re extending that capability to the desktop.

PHP 7.2.30 Release Announcement – PHP: Hypertext Preprocessor

The PHP development team announces the immediate availability of PHP 7.2.30. This is a security release.All PHP 7.2 users are encouraged to upgrade to this version.For source downloads of PHP 7.2.30 please visit our downloads page, Windows source and binaries can be found on windows.php.net/download/. The list of changes is recorded in the ChangeLog.

Taking your application to SaaS: a business decision – platform.sh

Why have some great apps not evolved to SaaS? With new technology they still can.
For digital agencies and established software vendors, this blog offers insight into how easy it is now to launch and manage a full cloud SaaS offering with a PaaS. In my last article, I shared the recent story of a white-label client who launched their software-as-a-Service cloud offer and grew in value from 2.5x to 12x revenue over two short years.

Managing Multiple PHP versions via the ondrej/php PPA – Matthew Weier O’Phinney

Last week, I did some system updates, and then decided to compile the most
recent PHP releases. I’ve used phpbrew to
manage multiple PHP releases for a number of years, and having it install a new
version is fairly routine.

Except this time, it wasn’t. Due to updates I installed, I was getting errors
first with compiling the GD extension, then with ext-intl:

  • If you want Freetype support in ext-gd, you are expected to install the
    package libfreetype-dev. On Ubuntu, this now installs libfreetype6-dev, which
    no longer includes the freetype-config binary that PHP’s configure script
    uses to determine what features it supports.

  • Similarly, ext-intl depends on the package libicu-dev. Ubuntu’s package now
    omits the icu-config binary used by PHP to determine feature support.

I searched for quite some time to find packages that would resolve these
problems. I could have found the source code and compiled it and linked to that,
but that would mean keeping that up-to-date on top of my PHP installs.

I even looked in the ondrej/php PPA, as that repository
has multiple PHP versions already, including source packages.

And then I thought: why not try using those instead of phpbrew?

The rest of this post is how I made that work.

I use Ubuntu for my operating system. The instructions I present here should
work on any Debian-based system, but your mileage may vary. If you are using
an RPM-based system, yum will be your friend, but I have no idea how to add
repositories in that system, nor if update-alternatives is available. As
such, these instructions may or may not help you.

Which is okay. I mainly wrote them to help future me.

Register the PPA

First, I had to add the PPA to the system:

$ sudo add-apt-repository ppa:ondrej/php

Then the usual:

$ sudo apt update

Approach to installation

I first identified the extensions I typically install, matched them to
packages in the PPA, and made a list. I knew I’d be installing the same
extensions across all PHP versions I wanted, so I figured I could script it a
bit.

From there, I executed the following from the CLI:

$ for VERSION in 5.6 7.0 7.1 7.2 7.3;do
for> for EXTENSION in {listed all extensions here};do
for for> sudo apt install php${VERSION}-${EXTENSION}
for for> done
for> done

This grabbed and installed each PHP I needed along with all extensions I wanted.

Switching between versions

To switch between versions, you have two options:

  • Use the version-specific binaries: /usr/bin/php5.6, /usr/bin/php7.0, etc.

  • Set a default via update-alternatives:

    $ sudo update-alternatives --set php /usr/bin/php5.6
    

    If you’re not sure what you have installed, use:

    $ sudo update-alternatives --config php
    

    which will give you a listing, and an ability to select the one to use.

Rootless alternatives

What if you’d rather not be root to switch the default version, though?
Fortunately, update-alternatives allows specifying alternate config and admin
directories.

Define the following alias in your shell’s configuration:

alias update-my-alternatives='update-alternatives \ --altdir ~/.local/etc/alternatives \ --admindir ~/.local/var/lib/alternatives'

Additionally, make sure you add $HOME/.local/bin to your $PATH; since
defining $PATH varies based on the shell you use, I’ll leave that for you to
accomplish.

If you open a new shell, the alias will now be available; alternately, source
the file in which you defined it to have it take effect immediately.

Once you’ve done that, you can run the following, based on the PHP versions
you’ve installed:

$ for VERSION in 5.6 7.0 7.1 7.2 7.3;do
for> update-my-alternatives --install $HOME/.local/bin/php php /usr/bin/php${VERSION} ${VERSION//./0}
for> done

This will create alternatives entries local to your own user, prioritizing them
by version; as a result, the default, auto-selected version will be the most
recently installed.

You can verify this by running update-my-alternatives --config php:

There are 5 choices for the alternative php (providing $HOME/.local/bin/php). Selection Path Priority Status
---------------

Truncated by Planet PHP, read more at the original (another 7476 bytes)