Tag: php

  • IMG Sizes Attribute Explained: Boosting Image Optimization

    IMG Sizes Attribute Explained: Boosting Image Optimization

    Your template’s design layout often has images that do not fill the entire width of all devices your visitors may be using. CSS’s grid layouts and flexbox layouts allow even greater control over the design of websites. Using img sizes attribute to hint to the browser the maximum size the image will render at allows developers to reduce bandwidth and fit images to their design rather than the device’s width.

    A number of articles discussing this idea, or at least something similar to it, talk about art direction but this idea is different. Most suggest wrapping the img element with a picture element with customized source elements. This allows you do do things like replace the image based on the device size, but requires duplicating the image URL in order to use it to simply define a maximum image size for the design based on device width. This is overkill for simple control of the image resolution.

    Content Management Systems (CMS) and blog software including Drupal modules, WordPress and it’s popular plugins have a default of using the device width. They have no understanding of the design’s layout. This is problematic as not all images would benefit from it and some, like icons and logos, may end up causing browsers to download the wrong resolution and leads to images that are either too large or too small for the space the image fills. For most CMSes, the best solution is to simply define them all to be based on the width of the device since it’s extremely rare that images placed on the site would exceed that resolution.

    For example, here is an image grid where the img sizes attribute is set to 100px:

    You can see the images are all low resolution, even on a desktop display. Now let’s set the img sizes attribute to 100vw (100% the viewport width):

    You can see that the images are now crisp and easy to view… but there’s still a problem. They are downloading very large images even though their width is 33% of the screen… minus the padding as well. The browser is downloading images far larger than it needs to. This uses more bandwidth than needed and is slow on mobile devices with slower wireless internet connections.

    Customize The IMG sizes Attribute For Each Image Element

    Each image element tag needs the sizes attribute added (or replaced) with the customized version. In some cases this can be tedious, but there are things you can do to make the process a little easier on yourself.

    Preset, Named img sizes Attribute Values

    One way to achieve this is to create a way to store predefined sizes attributes. Make it easier by giving them unique, simple names. For instance, consider a design that has a section that will show up to 5 columns on a 2K or greater screen, 4 columns on a 1920px desktop screen, 2 columns on a tablet, and a single column mobile phones. Here is an example: (max-width: 640px) 100vw, (max-width: 1024px) 50vw, (max-width: 1920px) 25vw, 20vw.

    You could call this value “5-column” with the value set to what you would like the img sizes attribute to be, and embed that into all of the img sizes attribute. Instead of trying to remember the value of sizes or copying and pasting it over and over again, you simply tell it to include the 5-column named value. This also has the additional benefit of being able to change many images’ sizes attribute at the same time if that section of the design were to change in the future.

    Additional img sizes Considerations

    Lightbox & Other Full-screen Modal Image/Gallery Scripts

    These scripts take an existing image and “blow it up” to display it full-screen. This is to make images that may be hard to see large enough that the viewer can recognize subjects and possibly text within the image. However, altering the img sizes attribute can potentially be problematic. For instance, your desktop design calls for a 300 pixel (px) wide display and the lightbox script full-screens that image. They appear horribly artifacted/pixelated when displayed at 300px wide on a 1920px wide screen. It would appear even worse on a 2K or 4K monitor. You want to make sure the img tag used by the enlargement script does not simply copy the sizes attribute you have assigned on the site.

    How to ensure browsers will download the full-screen version of images when they are enlarged? This is a tricky answer. There are a number of different lightbox and modal image enlargement scripts. Some are forks of existing scripts but and many are independently developed and lack the same features. In general, you want to make sure to have a custom sizes attribute available in this case. I don’t see any of the major scripts’ documentation showing any data-sizes attribute or similar, so you will likely need to hook the modal’s open event and swap out the sizes attribute yourself.

  • PHP INTL: Fix Missing Or Incorrect Time-zone Offset

    PHP INTL: Fix Missing Or Incorrect Time-zone Offset

    The INTL extension is great for showing translated strings as well as formatting dates and numbers in a way users in different locales expect. But sometimes IntlDateFormatter outputs something that isn’t correct or throws an error. Questions about incorrect or missing time-zone offsets are showing up more often. Let’s go over why that is and how you can fix it.

    When you format a date with a given time-zone, the INTL extension calls the International Components for Unicode (ICU) library. That library contains a vast data store of formatting expectations for many different locales (a combination of a countries and/or language). It also includes a list of time-zones, their names, abbreviations, offsets and when and if they use daylight saving time (DST). Except time-zones change periodically and in some cases, quite often.

    When do time-zones change?

    While most Western time-zones tend to be quite stable with few changes, others do not. The Middle East, Central & South America, Asia and parts of Europe have seen a number of changes over the years. Locations create new time-zones, remove others, and change others if or when they observe DST.

    There is no way to predict when these changes occur. Each country and even cities or regions determine when they are going to make a change. They (or others with knowledge of the change) then submit this information to the IANA to update their time-zone database (TZDB). Throughout the year new versions are released.

    How to fix a missing or incorrect time-zone

    The issue boils down to out-of-date data. Servers want to keep their software as stable as possible. Some are running software that is a decade or more old. It is possible to update the timezone information, even without administrative access.

    Identify the version of the ICU library that is currently in use by using phpinfo() or the command line to view the installed version.

    Screen capture of a phpinfo() function call showing the INTL extension and data version.

    Running version 54 or newer will allow you to drop-in the necessary files. Otherwise it will likely be more complicated.

    Next, download the latest time-zone data files:
    https://github.com/unicode-org/icu-data/tree/main/tzdata/icunew/

    To get the right files, browse to the newest directory. It is based on the year followed by a letter. The latest versions are followed by a /44. Within that directory, there are 3 others:

    • be (big endian)
    • ee (IBM mainframes using EBCDIC character sets)
    • le (little endian)

    Choose the endianness that fits your system. The vast majority will use little endian. Within that directory will be 4 files:

    • metaZones.res
    • timezoneTypes.res
    • windowsZones.res
    • zoneinfo64.res

    Download and place those files in a directory the web application has access to read. Finally, let the ICU library know to use these files for the time-zone database resource:

    PHP
    putenv('ICU_TIMEZONE_FILES_DIR=/path/to/tzdb/directory');

    Put this script as early as possible in your application. For instance, a bootstrap file or prepend directive. Depending on your server setup, you may need to restart the application and/or web server. The timezone you were missing or time that was incorrect will be correct.

  • PHP INTL In-Depth: Convert Numbers to Roman Numerals

    PHP INTL In-Depth: Convert Numbers to Roman Numerals

    In this PHP INTL In-Depth look, you will understand how to convert integer numbers to Roman numerals (1 => I, 2 => II, 3 => III, etc). There are a few different functions floating around the web on Stack Overflow and blogs. They use an array of values to convert numbers to Roman numerals, but are not needed. Before we get to the code, we need to know what these numerals are:

    Roman Numerals? What are those?

    So what exactly are Roman numerals? They are a numeral system: a number set represented symbols. Not to be confused with number systems, which are a set of specific numbers. A number system is a more abstract idea of a set of numbers, such as real or rational numbers, while a numeral system is how a set of numbers are expressed, such as decimals or binary in computer hardware.

    Roman numerals first appeared in ancient Rome. Europe continued to use until the Middle Ages after the Roman Empire fell. Afterward, they were replaced by the Arabic numerals or Latin digits (1, 2, 3, etc) used throughout much of the world today.

    Roman numerals are still used today but are an artistic choice. Examples of where they are still used are circular analog clock faces, company marketing or advertising campaigns, and buildings or monuments with engravings of years.

    Roman numerals are read as a basic mathematical expression. Smaller numerals positioned before a larger numeral are subtracted and numbers after are added. For instance, IIV represents the number 3 where V is equal to 5 and I is equal to 1, but since they are placed before the 5 they are subtracted. Numbers of the same or lesser value positioned after a larger number are added. For instance, MMXXIII which represents the year 2023.

    INTL: NumberFormatter Decimal Numbering System

    PHP’s INTL (internationalization or i8n) library comes with a number formatter class meant to format integers & floats into human-readable numbers, currencies, and accounting. However, there is also a numbering system built into it capable of a few different features. One of those features is Roman numerals.

    Instead of creating a function or method to convert numbers to numerals, there is a C/C++ library already compiled and wrapped by PHP: The INTL library. Within that library is a NumberFormatter class which provides access to the powerful ICU Unicode internationalization formatting class.

    The formatting class is very powerful and can be used to format a number in various ways. An input is always just a plain, bland, number such as 645002. However, humans have trouble understanding large groups of numbers without something to indicate how many positions there are.

    The NumberFormatter can be used to convert that number to a more understandable 645,002. In other countries a dot may be used as the separator and formatted as 645.002. It could also be used to spell out the number as six-hundred forty-five thousand two.

    In this case, we want the representation to be a Roman numeral. So we can tell the formatter to use Roman numerals by appending to or replacing the locale with a keyword:

    PHP
    function intToRomanNumeral(int $num) {
        static $nf = new NumberFormatter('@numbers=roman', NumberFormatter::DECIMAL);
        return $nf->format($num);
    }
    

    Here are some output examples:

    PHP
    echo intToRomanNumeral(2); // II
    
    echo intToRomanNumeral(5); // V
    
    echo intToRomanNumeral(10); // X
    
    echo intToRomanNumeral(50); // L
    
    echo intToRomanNumeral(57); // LVII
    echo intToRomanNumeral(58); // LVIII
    
    echo intToRomanNumeral(100); // C
    
    echo intToRomanNumeral(150); // CL
    
    echo intToRomanNumeral(1000); // M
    
    echo intToRomanNumeral(10000); // ↂ
    

    The formatter will also format zero as N and negative numbers the same as positive numbers but preceded by a Unicode minus symbol (not the hyphen/minus symbol on keyboards). There are technically no actual zero or negative Roman numerals. They didn’t exist back when the numeral system was developed so the inclusion of them is a modern representation.

    You can alternatively use the MessageFormatter class in the same way:

    PHP
    function intToRomanNumeral(int $num) {
        static $nf = new MessageFormatter('@numbers=roman', '{0, number}');
        return $nf->format([$num]);
    }
    

    There is a caveat: performance. In my own testing, this function is about 6 times slower than NumberFormatter. You can use it for individual formats within a string, but if you are planning on using it in a loop or with many messages, it will be more performant use NumberFormatter by itself or pass a numeral converted by NumberFormatter to the MessageFormatter arguments.

    I also posted these code examples on GitLab Snippets and Stack Overflow.

  • PHP 8.2’s Subtle New Features

    PHP 8.2’s Subtle New Features

    PHP version 8.2 was released on December 8, 2022 and I’m sure you’ve already heard about the great new features. The Randomizer & random generator engines classes, DNF types; constants within traits; the sensitive parameter trait; static return types for dates; the n regular expression modifier; false, true, and null types; the readonly class modifier, a handful of deprecations, and a small performance bump.

    These features are great. They add some new things to play with, fix a few issues, and provide the usual speed boost. What about the more subtle features that aren’t reported on. They need some love too.

    phpinfo()‘s Visual Upgrade

    The first thing I will go over is a feature that doesn’t appear to be reported on anywhere. Not even in the changelog. Why?

    The phpinfo() function got a facelift. It now supports dark mode using the OS and browser’s CSS prefers-color-scheme media query. There is also now sticky headers for tables. The headers will stay at the top of the screen while you continue scrolling.

    Some other new features that go unnoticed…

    • Opimized code path for newly created files using the file:// stream wrapper.
    • Lower memory usage for strings created by various different functions.
    • Added support for curl’s CURLOPT_XFERINFOFUNCTION progress callback option. This replaces the deprecated CURLOPT_PROGRESSFUNCTION option.
    • Added support for curl’s CURLOPT_MAXFILESIZE_LARGE option which sould be used instead of CURLOPT_MAXFILESIZE in cases where the file is greater than 2 gigabytes.
    • Added curl_upkeep(callable) function that accepts a callable (function/method) and is used when HTTP2 keep-alive/ping requests are sent.
    • The idate(format, timestamp) function now supports the “N” and “o” specifiers to get the ISO day of week and ISO year respectively.
    • Added the FILTER_FLAG_GLOBAL_RANGE option when validating with the FILTER_VALIDATE_IP flag in the filter functions. The filter will fail when non-global IPV4/IPV6 IP ranges are used in accordance with RFC 6890 where the Global attribute is false.
    • A number of Apple Mac-specific memory and JIT compiler-related improvements.
    • Added JIT indirect call reduction.
    • Added the openssl_cipher_key_length(algorithm) function.
    • Added the ReflectionFunction::isAnonymous() method which returns true if the function is anonymous (e.g. a closure).
    • Added the ReflectionMethod::hasPrototype() method which returns true if the method has an inherited parent method (prototype).
    • Added a number of new Socket options.
    • Added ini_parse_quantity(string_value) function to convert parsed shorthand ini values that may include a number of supported units to integer value.
    • Added memory_reset_peak_usage() function to allow checking for excess memory usage multiple times throughout a script.

    Upgrade Soon!

    PHP 8.2 has many features that will make development and testing with PHP easier. It’s easy to overlook the more suble and less-reported-on features, but they are often some of the more useful or awaited changes. It’s highly reccomended that you upgrade to version 8.2 soon and always keep your software versions up-to-date to get the latest features and security fixes.