Derick RethansPHP Internals News: Episode 23: Deprecated Short Open Tags, again (15.8.2019, 08:23 UTC)

PHP Internals News: Episode 23: Deprecated Short Open Tags, again

In this episode of "PHP Internals News" I chat with George Banyard (Website, Twitter, GitHub, GitLab) about his second RFC "Deprecate Short Open Tags, again".

The RSS feed for this podcast is, you can download this episode's MP3 file, and it's available on Spotify and iTunes. There is a dedicated website:


Music: Chipper Doodle v2 — Kevin MacLeod ( — Creative Commons: By Attribution 3.0

Become a Patron!
Rob AllenDisplaying exif information in WordPress posts (14.8.2019, 10:01 UTC)

After discovering that WordPress modifies img tags when rendering a page, it crossed my mind that I could display exif information underneath each image on my new photography blog. The basic process is applicable to any manipulation of the content that you would want to do before it is displayed.

To do this, we add a new filter to the the_content hook:

add_filter('the_content', function ($content) {
        // manipulate $content
        return $content;

As with all filters, we have to return data in the same format as the first parameter to our function. For the_content, we receive a string which is the post's article text.

The core operation

In my case, all my <img> tags are immediately followed by a <figcaption> tag as I have a caption under each one. I decided to add the exif data within the <figcaption>.

We use regex to find the tags:

if (!preg_match_all( '@img [^>]+><figcaption>[^<]*</figcaption>@', $content, $matches)) {
  return $content;

This will store each <img> tag that's followed by a <figcaption> tag into the $matches variable, so we can now iterate over it:

foreach ($matches[0] as $match) {
  // Find the url in the  tag
  if (preg_match('@src="([0-9a-z:/._-]+)@i', $match, $srcList)) {
    $url = $srcList[1];
    if ($url) {
      $postId = attachment_url_to_postid($url);
      if ($postId) {
        $exif = ffp_create_exifcaption($postId);
        $new = str_replace('</figcaption>', $exif .'</figcaption>', $match);
        $content = str_replace($match, $new, $content);

This code iterates over each match and finds the url in the src attribute of the image tag. We can then then load the post from that url using the rather helpful $postId = attachment_url_to_postid($url);.

Now that we have an ID, we can create the EXIF caption in a separate function and then finally we append it to the end of the current caption. We do this by simply replacing the closing </figcaption> with our text followed by </figcaption> which is a nice fast operation.

Creating EXIF display string

To create the string of EXIF data, I used a separate function to make it easier.

WordPress has already extracted the EXIF data we require when the image was uploaded into the admin, so we simply need to read it and format it. To get it we do this:

$metadata = wp_get_attachment_metadata($postId);
if (!isset($metadata['image_meta'])) {
  return '';
$imagemeta = $meta['image_meta'] ?? [];

$imagemeta is an array of useful characteristics about the image.

We pull the info we care into a set of variables like this:

$camera       = $imagemeta['camera'] ?? null;
$lens         = $imagemeta['lens'] ?? null;
$aperture     = $imagemeta['aperture'] ?? null;
$shutter      = $imagemeta['shutter_speed'] ?? null;
$iso          = $imagemeta['iso'] ?? null;
$focal_length = $imagemeta['focal_length'] ?? null;

if (! ($camera || $lens || $aperture || $shutter || $iso || $focal_length)) {
  return '';

(Obviously, if there is no data, there's nothing to do.)

Finally we format it nicely:

$string = '<aside class="exif">';

if ($camera) {
  $string .= '<div><ion-icon name="camera"></ion-icon><span>'.$camera.'</span></div>';
if ($lens) {
  $string .='<div><ion-icon name="radio-button-on"></ion-icon><span>'.$lens.'</span></div>';

if ($aperture) {
  $aperture = 'f/' . $aperture;
  $string .= '<div><ion-icon name="aperture"></ion-icon><span>'.$aperture.'</span></div>';

if ($shutter) {
  $shutter = '1/' . (int)(1.0/(float)$shutter);
  $string .= '<div><ion-icon name="timer"></ion-icon><span>'.$shutter.'</span></div>';

if ($iso) {
  $string .= '<div><span class="text-icon">ISO</span><span>'.$iso.'</span></div>';

if ($focal_length) {
  $focal_length = (string)(round((float)$focal_length)) . 'mm';
  $string .= '<div><ion-icon name="eye"></ion-icon><span>'.$focal_length.'</span></div>';

$string .= '</aside>';
return $string;

We wrap our entire EXIF string in an <aside> tag so that we can style appropriately. Then for each item of data, we display an icon followed by the text information. The icons are Io

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

Voices of the ElePHPantInterview with Jeremy Mikola (13.8.2019, 15:30 UTC)
Evert Pot506 Variant Also Negotiates (13.8.2019, 15:00 UTC)

In 1998 RFC2295 was published. It’s experimental, and meant to introduce a new way to do content negotiation in HTTP. As far as I personally know, I don’t think it got a lot of traction.

Traditionally, when a HTTP client wants to do content-negotation, they will send one or more accept headers:

GET / HTTP/1.1
Accept: text/html; image/png; text/*; q=0.9
Accept-Language: en-CA; en
Accept-Charset: UTF-8
Accept-Encoding: gzip, brotli

RFC2295 intended to introduce a new way to do this, with a lot more flexibility and features. The RFC talks about selecting specific variants not just based on mimetype, but also HTML features a browser supports, color capabilities, screen resolution, speed preference, paper size for printers and even selecting content for specific devices like VR goggles and PDA’s.

An interesting feature is that it can also return a list of urls for specific variations, changing the HTTP model a bit by giving every representation and variant their own url, and returning all this in with a 300 Multiple Choices response.

An example of such a response (from the RFC):

HTTP/1.1 300 Multiple Choices
Date: Tue, 11 Jun 1996 20:02:21 GMT
TCN: list
Alternates: {"paper.1" 0.9 {type text/html} {language en}},
{"paper.2" 0.7 {type text/html} {language fr}},
{"paper.3" 1.0 {type application/postscript}
{language en}}
Vary: negotiate, accept, accept-language
ETag: "blah;1234"
Cache-control: max-age=86400
Content-Type: text/html
Content-Length: 227

<h2>Multiple Choices:</h2>
  <li><a href=paper.1>HTML, English version</a>
  <li><a href=paper.2>HTML, French version</a>
  <li><a href=paper.3>Postscript, English version</a>

The RFC introduces a new error code: 506 Variant Also Negotiates. To the best of my understanding, this error returned when a server is misconfigured and a ‘negotiating resource’ is pointing to another resource that doesn’t serve a representation, but instead also tries to negotiate.

I can imagine that a negotiating resource could for example point to itself, or sets up something like a redirection look. I think 506 is a specific error that a server could return for this case.

Should you use this?

I often include a section that answers whether you should use this status code. In this case, I think it’s better to dive into whether you should support the negotiation feature.

The issue with the feature is that it never left the experimental phase, and as far as I know got very little adoption. It was defined before HTTP/1.1 was finalized, and for all intents and purposes I think it can be considered dead.

However, it solves a couple of really interesting problems that aren’t solved

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

Voices of the ElePHPantInterview with Matthew Turland (13.8.2019, 11:53 UTC) Link
Derick RethansPHP Internals News: Episode 22: PostGreSQL Escape Character (8.8.2019, 08:22 UTC)

PHP Internals News: Episode 22: PostGreSQL Escape Character

In this episode of "PHP Internals News" I chat with Matteo Beccati (LinkedIn, Twitter, GitHub) about his "Escape PDO "?" parameter placeholder" RFC.

The RSS feed for this podcast is, you can download this episode's MP3 file, and it's available on Spotify and iTunes. There is a dedicated website:


Music: Chipper Doodle v2 — Kevin MacLeod ( — Creative Commons: By Attribution 3.0

Become a Patron!
PHP: Hypertext PreprocessorPHP 7.4.0beta2 released! (8.8.2019, 00:00 UTC)
The PHP team is glad to announce the second beta release of PHP 7.4: PHP 7.4.0beta2. This continues the PHP 7.4 release cycle, the rough outline of which is specified in the PHP Wiki. For source downloads of PHP 7.4.0beta2 please visit the download page. Please carefully test this version and report any issues found in the bug reporting system. Please DO NOT use this version in production, it is an early test version. For more information on the new features and other changes, you can read the NEWS file, or the UPGRADING file for a complete list of upgrading notes. These files can also be found in the release archive. The next release would be Beta 3, planned for August 22nd. The signatures for the release can be found in the manifest or on the QA site. Thank you for helping us make PHP better.
Rob AllenImages and WordPress (7.8.2019, 09:02 UTC)

My new WordPress project has multiple photographs per post and as I wanted them to work in an efficient manner for multiple screen resolutions. The secret to this is the srcset and sizes attributes on the img tag.

It turns out that WordPress will create multiple sized thumbnails when you upload an image. It will also add the srcset and sizes attributes into your img tags for you if your image tag has a class of wp‑image‑{id} where {id} is the id of the image in the database.

Image sizes

The set of sizes of images that WordPress creates by default is rather small (you get widths of 150px, 300px, 768px, & 1024px). As I'm uploading 6000px wide photos, having some intermediate sizes greater than 1024 is useful to reduce the amount of data for desktop screens.

You can add new ones using the add_image_size() method which is best done after the theme is set up:

add_action('after_setup_theme', static function () {
    add_image_size('xl', 1500);
    add_image_size('2xl', 2000);
    add_image_size('3xl', 2500);
    add_image_size('4xl', 3000);
    add_image_size('5xl', 4000);
    add_image_size('6xl', 5000);

The third parameter to add_image_size() is the height constraint, which I don't set as I don't want it to be taken into account for portrait orientation pictures.

Note that even though you've added additional sizes, WordPress will ignore any bigger than 1600 pixels unless you tell it otherwise by adding a filter to max_srcset_image_width:

add_filter('max_srcset_image_width', static function($max_width){
    return $max_width < 6000 ? 6000 : $max_srcset_image_width;

If you change the set of image sizes after you've uploaded some images then you can create the new images using the wp‑cli media regenerate command or install the Regenerate Thumbnails plugin and run it from the Tools menu in your admin.

Adding wp‑image‑{id} class to posts

If you use the WordPress admin site to create your posts, then it automatically set the wp‑image‑{id} class for you on our img tags. I use MarsEdit, a desktop client that uses the XML-RPC API, for creating my articles and it doesn't set this class yet, so I needed to write a plug in for this.

To do this, we put a filter on the xmlrpc_wp_insert_post_data hook to inspect the content of the post and update if needed.

add_filter('xmlrpc_wp_insert_post_data', function ($data) {
  if ($data['post_type'] ?? '' === 'post') {
    $content = wp_unslash($data['post_content'] ?? '');

    // Find all <img> tags to add the "wp-image-{id}" class
    if (preg_match_all('/<img.*>/im', $content, $images)) {
      foreach ($images[0] as $img) {
        // Find the url in the src attribute
        if (preg_match('/src="([^"]+)/i', $img, $srcList)) {
          // Retrieve the id for this image.
          $postId = attachment_url_to_postid($srcList[1]);
          if (!$postId) {
            // This image isn't in the database, so don't touch it

          // Add the wp-image-{id} class if it doesn't exist for this image
          $list = [];
          if (stripos($img, 'class') === false
            || preg_match('/class="([^"]+)/i', $img, $list)) {
            $classes = $list[1] ?? '';
            $hasClassAttribute = (bool)count($list);

            if (!preg_match('/wp-image-([0-9]{1,10})/i', $classes)) {
              // wp-image-{id} class does not exist on this img
              $classes .= ' wp-image-' . $postId;

              if ($hasClassAttribute) {
                // Update the img tag with the new class attribute
                $newImg = preg_replace('/class="[^"]*/', 'class="'.$classes, $img);
              } else {
                // Insert class attribute into the img tag
                $newImg = str_replace('<img ', '<img class="'.$classes.'" ', $img);
              // Replace the original <img> with our updated one
              $content = str_replace($match, $newImg, $content);



    $data['post_content'] = wp_slash($content);

  return $data;

Note that the post's content

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

Voices of the ElePHPantInterview with James Titcumb (6.8.2019, 23:01 UTC)
Voices of the ElePHPantInterview with Jeff Geerling (6.8.2019, 16:03 UTC)
LinksRSS 0.92   RDF 1.
Atom Feed   100% Popoon
PHP5 powered   PEAR
ButtonsPlanet PHP   Planet PHP
Planet PHP