Thiemos Archiv
- Wednesday, 2020-10-07 20:30
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 viaarray_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 additionaltrue
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 ofexplode()
possible returning fewer elements. While we can specify an upper limit for the number of elementsexplode()
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 afile_exists()
, reading a file'sfilemtime()
, 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 whatdate()
andstrftime()
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, thegrapheme_strlen()
of π¨βπ©βπ¦ (a family with a man, a women, and a boy) is 1, whilemb_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 ofmicrotime( true )
(seconds).http_response_code( 404 )
is a modern alternative for the commonheader( 'HTTP/1.0 404 Not Found' )
that is still documented everywhere.http_response_code()
exists since PHP 5.4. You might still want to useheader()
in case you need to set an odd code, or a custom text.idate()
is an alternative todate()
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 isidate( '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 with7 % 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 ifcount()
will return a meaningful result for a value. This is the case for arrays and objects implementing theCountable
interface. Note that usingCountable
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 longif ( is_array( $x ) || $x instanceof Traversable )
checks we need to make sure we can iterate something in aforeach
loop. There is even aniterable
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 implementTraversable
.What's the point of having
is_countable()
as well asis_iterable()
? Well, not every resource that can be used inforeach
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 valuesNAN
,INF
, or-INF
. This often makesis_finite()
the better, less suprising alternative tois_double()
. For example,1 / 0
isINF
, but it's type is still a double.is_double( 1 / 0 )
is true, butis_finite( 1 / 0 )
is not.For comparison: The same exists in JavaScript as
Number.isFinite( 1 / 0 )
, as well as a top-level functionisFinite()
. However, the later casts values to numbers first, and might behave unexpected because of this.max()
: Do you know thatmax()
andmin()
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 ofpreg_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 totoPrecision()
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 whatstr_split()
is for. No.chunk_split()
cuts a string into fixed-length chunks (unlikewordwrap()
, 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 ofextract()
(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 assqrt( $x * $x + $y * $y )
. Why?lcg_value()
is yet another random number generator. There are at least 3 others. Why?
Kommentare zu diesem Beitrag können per E-Mail an den Autor gesandt werden.
scnr