41
\$\begingroup\$

What general tips do you have for golfing in PHP? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to PHP (e.g. "remove comments" is not an answer). Please post one tip per answer.

\$\endgroup\$
2
  • \$\begingroup\$ Wait, am I doing it right?... Anyway, I'm really curious about this one. PHP is used by many people and golfers, but I almost have no idea how to golf a PHP code. \$\endgroup\$
    – JiminP
    Jun 20, 2011 at 14:11
  • \$\begingroup\$ Use short tags <??> It can save a few bytes. \$\endgroup\$
    – Mob
    Aug 12, 2011 at 20:49

40 Answers 40

1
2
1
\$\begingroup\$

str_repeat

In some cases you have a input of characters and you should output them repeated with an input greater zero for each characters.

for(;--$z?:($c=$argn[$i++]).$z=$argn[$i++];)echo$c;

(52 bytes) is shorter than

for(;~$c=$argn[$i++];)echo str_repeat($c,$argn[$i++]);

or

for(;~$c=$argn[$i++];)echo str_pad($c,$argn[$i++],$c);

(54 bytes each)

How it works for example input a1b2c1

$z is not set (implicit NULL), so --$z does nothing and is falsy;

$c="a", $z="1" and $i=2 -> $c.$z="a1" is truthy -> output "a"

--$z=0; so we set $c="b", $z="2" (and $i=4) -> $c.$z="b2" is truthy -> output "ab"

--$z=1 -> output "abb"

--$z=0; so we set $c="c" and $z=1 $c.$z="c1" is true output "abbc"

--$z=0 so $c="" and $z="" -> $c.$z="" is falsy -> loop breaks

\$\endgroup\$
1
\$\begingroup\$

Removing characters in a string

join(explode(" ",$string));

saves 1 character compared to

str_replace(" ","",$string);
\$\endgroup\$
7
  • \$\begingroup\$ Note that this works for all (nonempty) strings, not just characters. \$\endgroup\$ Jun 23, 2017 at 16:58
  • \$\begingroup\$ @CalculatorFeline Why should it not work for empty strings? It make no sense or this case. \$\endgroup\$ Jun 23, 2017 at 17:02
  • \$\begingroup\$ Well, the first version doesn't work with "" and it's not very useful anyway. \$\endgroup\$ Jun 23, 2017 at 17:04
  • 1
    \$\begingroup\$ @CalculatorFeline And for this case a zero byte solution is much better. It make no sense to do this in that way. \$\endgroup\$ Jun 23, 2017 at 17:17
  • 3
    \$\begingroup\$ Your join example is missing a ). And strtr($string,[" "=>""]) is even shorter. \$\endgroup\$
    – Titus
    Oct 10, 2017 at 8:23
1
\$\begingroup\$

Little-known tricks with increment/decrement

$x = 'a';
echo ++$x; // Outputs "b"

$x = 'z';
echo ++$x; // Outputs "aa"

And many more at the following RFC, showing they will be removed/normalized in PHP 8.3

https://wiki.php.net/rfc/saner-inc-dec-operators

\$\endgroup\$
0
\$\begingroup\$

use deprecated functions
If you can use POSIX instead of PERL regex without wasting more than 5 bytes on the expression, use ereg or eregi instead of preg_match, split or spliti instead of preg_split.
split Can also be used as a synonym for explode for most delimiters.

These functions are marked deprecated and will throw E_DEPRECATED notices, but (cannot find the source now) I think I have read that warnings and notices are ok.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Watch out! These were removed in PHP 7.0. \$\endgroup\$
    – Umbrella
    Sep 7, 2018 at 18:34
0
\$\begingroup\$

Use short append syntax to add elements to the end of an array.

Instead of keeping track of the number of elements:

$array[++$i] = $value;

Or using the explicit push function:

array_push($array, $value);

You can use the implicit push feature:

$array[] = $value;
\$\endgroup\$
2
  • 1
    \$\begingroup\$ With a rare exception: when you need to append more than 3 elements, array_push($array, $value, $value, $value, $value); may be shorter; \$\endgroup\$
    – manatwork
    Jan 5, 2017 at 19:37
  • \$\begingroup\$ Heh yeah, also note, if $value is itself an array, it's probably not going to do what you want. \$\endgroup\$ Jan 5, 2017 at 19:42
0
\$\begingroup\$

JSON encode static array definitions

JSON encoding is one byte shorter per element than native PHP encoding for associative arrays. Of course, then you have to quote the resulting string (an additional two chars) and call json_decode() on that string (an additional 13 chars.) So, if you have a static associative array with more than 15 elements, try JSON encoding it. Note this probably won't work if you can use the "unquoted string literals" trick in your PHP array definition.

Instead of:

$array = [...];

Use:

$array = json_decode('...');
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I think explode is also worth a shot. \$\endgroup\$
    – Titus
    Jan 6, 2017 at 0:08
0
\$\begingroup\$

Switches base on arrays or strings

A little collection which can save Bytes for different conditions

Switch a function through an array

<?=(bc.[sub,add,mul,pow][($x=$argn)&3])($x,$x);

Make different Math operations

<?=[0,($x=$argn)+$x,$x*$x,$x**$x][$x&3];

Ternary Operator to switch a function

echo(strto.(rand()&1?low:upp).er)($c);

Using the Keys in an array as conditions

echo[ctype_alpha($s=dechex($argn))=>"Only letters",ctype_digit($s)=>"Only numbers",Mix][1];

special on strings but only not in all cases you must use a function and trim will do nothing

($bool?trim:strtolower)($string)

character switch

"char"[$x%4]

character only in zero cases

"c"[$x%2]
\$\endgroup\$
0
\$\begingroup\$

I didn't see it listed so when using a list and explode you can use a number as the delimiter ( assuming its not in the string of course ). This lets you get rid of the quotes.

If there is backslash before the delimiter then it has to be 8 or 9 so it doesn't get confused with octal ( such as \0 )

So instead of:

explode(",", "one,two,buckle,my,shoe")

You can do this

explode(1, "one1two1buckle1my1shoe")

Saving 2 bytes for the quotes.

As I mentioned for backslashes

explode(",", "/ \,| .. |,\ /");

Use 8 or 9

explode(8, "/ \8| .. |8\ /");  // so you get \8 instead of \0 etc..
\$\endgroup\$
0
\$\begingroup\$

seek potential in combining several expressions to one

It´s easy to see that A?B:C; is shorter than if(A)B;else C;
A&&B is shorter than if(A)B; and A||B; is shorter than if(!A)B;

But how can if(A){B;C;}else{D;E;} be golfed?

Assume that B returns something truthy and D something falsy:

if(A){B; C;}else{B; C;}  # 21 bytes
A?    B&&C  :    B||C;   # 12 bytes

another one:

if(A){$b=123;$c="hello";} # 25 bytes
A&&$b=123+!$c="hello":    # 22 bytes
A&&$c="hello".!$b=123;    # also 22 bytes

$c="hello is truthy, so !$c is false. + casts to int; so we add 0 to 123.
$b=123 is truthy, so !$b is false. . casts to string, and that´s empty for false.

I even had a case today where appending digits to the string didn´t make a difference.
(See line 5 of the breakdown.)

more examples:

for(;;$a++,print$x);
for(;;$a+=print$x);

print always returns true, so its return value can be added to $a instead of incrementing $a separately.

$a=1,$b=0
$a=!$b=0

if(X){$a=123;$b=0;}
X&&$a=123+$b=0;     # useful if you need $b to be and int (for printing)
X&&$b=!$a=123;

if(X){$a=123;$b=1;}
X&&$b=!!$a=123;
X&&$a=123*$b=1;

advanced: combining nested loops

takes a new level to combining expressions. There is no general solution or approach; and it doesn´t always make the code shorter. Good chances are when the outer loop has no body except the inner loop; imagine shifting through a 2D array or sorting one.

Try

for($a=1;$a<20;$a+=print"\n")for($b=$a;$b++<20;)echo$a*$b,"\t";

61 bytes (counting the control characters as one each)
that loop $a from 1 to 19, $b from $a+1 to 20
and print the products, breaking the line when $a increments.

Ok, one simple two-byte saving is

for($a=1;20>$b=$a;$a+=print"\n")for(;$b++<20;)echo$a*$b,"\t";

Combining the two loops to one saves another two bytes:

for($a=$b=1;$b++<20||21>$b=1+$a+=print"\n";)echo$a*$b,"\t";

And you can save two more bytes with a leading linebreak instead of a trailing one (this often saves a byte or two):

for($b=20;$b++<20||21>$b=1+$a+=print"\n";)echo$a*$b,"\t";

This is a pretty easy and rewarding example; so don´t be upset if your first attempts fail on saving bytes. It may take a while until you get an eye for it.

Ok, for(;20>$b=++$a;print"\n")for(;$b++<20;)echo$a*$b,"\t"; is golfier; and in PHP 7, you can even avoid trailing tabs without any costs: for(;20>$b=++$a;)for(;$b++<20;)echo$a*$b,"\n\t"[$b<20];

But imagine you have to loop $a from somewhere else but 1, like through ascii codes.

I may one day come up with an example that makes more sense. No promises on these premises though.

\$\endgroup\$
1
  • \$\begingroup\$ primo also posted on combining loops in March 2014. Don´t miss out on that one! \$\endgroup\$
    – Titus
    Feb 22, 2019 at 15:26
0
\$\begingroup\$

When using array_values() to re-index an array with numeric keys, from PHP7.4 you can use a technique that I call splatpacking (see link for further details and a demonstration).

You use the "splat/spread" operator to "unpack" the array, then immediately "repack" the elements into an empty array. Though not designed for this purpose, the result is the same -- in that limited scenario.

Technique using $a representing an array:

array_values($a)  // 14 characters (not counting $a)

becomes

[...$a]  // 5 characters (not counting $a)
\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.