maettig.com

Thiemos Archiv

Some lesser known PHP gems

… in alphabetical order.
  • array_change_key_case( $array, CASE_LOWER ) unifies the capitalization of string keys in an array to either lowercase (default) or uppercase.
  • array_column() got it's own blog post already.
  • array_combine( $keys, $values ) might not have the best name. What does it mean to »combine« arrays? It brings array keys and values together. The function requires both source arrays to have the same number of elements. Keys in the two source arrays are ignored, i.e. array_combine() behaves as if the keys are stripped via array_values(). The values in the first source array – that's what's going to be used as keys – must be integers or strings, and are cast to strings otherwise.
  • array_fill_keys( $keys, $value ) is very similar, combining keys and values from two different sources. But here all elements get the same value. We can use this to reset all elements in an array to 0, for example, without knowing anything else about the elements: $array = array_fill_keys( array_keys( $array ), 0 ). This is quite short, but possibly hard to read. A loop that does the same can be super short as well:
    foreach ( $array as &$value ) {
        $value = null;
    }
  • array_keys( $array, $searchValue, true ) doesn't just return all array keys, but only keys for elements that match the given value (with the additional true enabling strict, type-safe comparisons). That's rather powerful – well, in specific situations. Let's say you have a map of user names and if they are »admin« or something else. This will only return the names of admin users:
    array_keys( [ 'me' => 'admin', 'other' => 'newbie' ], 'admin' )
    You can think of it as a more powerful variant of array_search(), which returns the key of the first match only.
  • array_pad() is a way to deal with the uncertainty of explode() possible returning fewer elements. While we can specify an upper limit for the number of elements explode() returns, there is not much more we can be sure about. Well, the first element is guaranteed to be there, no matter if the delimiter even appears in the input, or how often. But we still need to check every other element. This can easily fail:
    [ $first, $second, $third ] = explode( ':', $input, 3 );

    We have to do it like this:

    $parts = explode( ':', $input, 3 );
    $first = $parts[0];
    $second = $parts[1] ?? null;
    $third = $parts[2] ?? null;

    Or utilizing array_pad():

    [ $first, $second, $third ] = array_pad( explode( ':', $input, 3 ), 3, null );

    Here is another hack to achive more or less the same:

    [ $first, $second, $third ] = explode( ':', $input . '::', 4 );

    Here missing elements don't become null, but empty strings. The trick is to append just enough delimiters so that the resulting array can never be to short. In this example we expect 3 elements, separated by 2 delimiters. This is what we append to be sure there are enough delimiters, even if the input is empty. Note the maximum number at the end must be one higher, or omitted. The extra element is needed to get rid of stray delimiters that would otherwise end in our result.

  • clearstatcache() clears the file status cache that is build up whenever you want to know something about a file, e.g. if a file_exists(), reading a file's filemtime(), and such. Consecutive calls to these functions might return old, cached values, ignoring changes done by external processes. You need to clear this cache to be able to see if a file was created, removed, or changed in the meantime. You can limit the effect to a specific file, if you want.
  • date_parse_from_format() allows to parse a date and time string according to a specific format. This is similar to what date() and strftime() do, but reversed.
  • grapheme_strlen() calculates the »visible« length of a Unicode string. This is relevant when multiple Unicode code points are used to »construct« characters, e.g. certain emoji, or when adding accents to characters that don't have them. For example, the grapheme_strlen() of πŸ‘¨β€πŸ‘©β€πŸ‘¦ (a family with a man, a women, and a boy) is 1, while mb_strlen() is 5 (because it's actually made of 5 Unicode characters, 3 individual people and 2 zero-width joiners).
  • hrtime( true ) (nanoseconds) is recommended when benchmarking code, instead of microtime( true ) (seconds).
  • http_response_code( 404 ) is a modern alternative for the common header( 'HTTP/1.0 404 Not Found' ) that is still documented everywhere. http_response_code() exists since PHP 5.4. You might still want to use header() in case you need to set an odd code, or a custom text.
  • idate() is an alternative to date() that does not return a string, but different integer numbers. It's limited because of this, e.g. it can return the year, but not the name of the month. A unique feature is idate( 'B' ) which returns the so called »internet time« a.k.a. Swatch »Beats«.
  • intdiv( 7, 4 ) returns 1 as an integer, always discarding fractions. This goes along with 7 % 4, which returns 3, the remainder of the same integer division, a.k.a. »modulo«.
  • is_countable() was added in PHP 7.3 and tells if count() will return a meaningful result for a value. This is the case for arrays and objects implementing the Countable interface. Note that using Countable as a type hint refers to the interface, and does not include arrays.
  • is_iterable() was added in PHP 7.1 and finally gives us a nice alternative for the overly long if ( is_array( $x ) || $x instanceof Traversable ) checks we need to make sure we can iterate something in a foreach loop. There is even an iterable type hint we can use to do the same. This is not an interface but a pseudo-type that accepts both arrays and objects that implement Traversable.

    What's the point of having is_countable() as well as is_iterable()? Well, not every resource that can be used in foreach can be counted. It might be a stream, returning one element after the other, without ever knowing when it will end.

    The other way around – something that is countable but not iterable – is technically possible, but weird. Why would one need to do this?

  • is_finite() is convenient to check if a floating point number (a.k.a. double) really is a floating point number, and not one of the special values NAN, INF, or -INF. This often makes is_finite() the better, less suprising alternative to is_double(). For example, 1 / 0 is INF, but it's type is still a double. is_double( 1 / 0 ) is true, but is_finite( 1 / 0 ) is not.

    For comparison: The same exists in JavaScript as Number.isFinite( 1 / 0 ), as well as a top-level function isFinite(). However, the later casts values to numbers first, and might behave unexpected because of this.

  • max(): Do you know that max() and min() do not only work with a list of values, but also with an array?
  • pathinfo( $filename, PATHINFO_EXTENSION ) is a lesser-known way to get a file name's extension, without the dot.
  • preg_filter() is a variant of preg_replace(). Both can perform replacements on an array of strings. Additionally, preg_filter() returns only elements that had a match.
  • preg_grep() performs a regular expression on an array of strings, and returns an array with the elemens that match the pattern.
  • round() accepts a precision, i.e. number of digits after the decimal point. This can as well be negative. We can use this to round to a specific amount of significant digits, similar to toPrecision() in JavaScript. Here we always get 3 significant digits, no matter how big the number is: round( $n, 3 - floor( log10( $n ) ) - 1 ).

PHPWTF

For this post, I reviewed the entire list of all PHP functions again, 17 years later. Some of the »features« you can find in this list are just … unbelievable.

Don't use these.

  • bbcode_create() is an actual parser for BBCode. And a really sophisticated one, even including fancy error correction, similar to what HTML Tidy does. Still, why?
  • chunk_split() sounds like … wait, what? Ok, it expects a string (why doesn't it say that?). But it doesn't split the string into »chunks« – that's what str_split() is for. No. chunk_split() cuts a string into fixed-length chunks (unlike wordwrap(), which respects word boundaries), inserts newlines (or whatever you want), and returns the result as a single, merged string. This is so utterly specific, who knows what it is for?
  • compact() is the opposite of extract() (see below), and as crazy. You give it a list of variable names. No, not variables. Variable names: $array = compact( 'color' ) does the same as $array = [ 'color' => $color ], but doesn't actually reference the variable $color. Please, don't do this.
  • date_sun_info() calculates sunrise and such at a specific location. That's … a PHP extension. Why is this a PHP extension?
  • extract() turns array elements into variables. For example, extract( [ 'color' => 'blue' ] ) creates a variable $color. Seriously, don't do this. It's unreadable. Use array decomposition via [ 'color' => $color ] = $array instead, or simply $color = $array['color'].
  • hypot() calculates the hypotenuse of a triangle. Ugh. There is a dedicated function that does the same as sqrt( $x * $x + $y * $y ). Why?
  • lcg_value() is yet another random number generator. There are at least 3 others. Why?
my favorite is still array_merge
scnr
DAT
Nice, thanks. What you are looking for is array_replace(). It was introduced in PHP 5.3, released just a few days before you wrote that post. I found this nice post comparing array_merge() vs. array_replace() vs. +. Yea, that's crazy. I try to avoid all of them.
Thiemo

Kommentare zu diesem Beitrag können per E-Mail an den Autor gesandt werden.

[ ← Zurück zur Übersicht ]

Impressum & Datenschutz