37
\$\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\$
  • \$\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 '11 at 14:11
  • \$\begingroup\$ Use short tags <??> It can save a few bytes. \$\endgroup\$ – Mob Aug 12 '11 at 20:49

39 Answers 39

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\$
  • \$\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 '17 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\$ – Alex Howansky Jan 5 '17 at 19:42
0
\$\begingroup\$

Use negative indexes to reference the end of a string

If you need the last character in a string, you can use the array reference method, and provide a negative index:

$lastChar = $string[-1];

This also works for functions like substr():

$lastFour = substr($string, -4);
\$\endgroup\$
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
    \$\begingroup\$ I think explode is also worth a shot. \$\endgroup\$ – Titus Jan 6 '17 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\$

Return two copies of an expression

If you want to get two copies of the result of an expression, you can use this:

$v.$v=expr

But it won't be shorter than the more straightforward ($v=expr).$v if you need to append more things to the result.

For 4 copies:

$v.$v.=$v=expr

You cannot use it to get 3 copies.

\$\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\$
  • \$\begingroup\$ primo also posted on combining loops in March 2014. Don´t miss out on that one! \$\endgroup\$ – Titus Feb 22 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\$
0
\$\begingroup\$

You may not always require echo, for instance:

 <?php $a=f();if($a)echo'blah';

Vs

 <?php $a=f();if($a)?>blah
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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