Tips for golfing in PHP

37

19

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.

JiminP

Posted 2011-06-20T14:10:00.947

Reputation: 3 264

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. – JiminP – 2011-06-20T14:11:23.850

Use short tags <??> It can save a few bytes. – Mob – 2011-08-12T20:49:44.240

Answers

22

Understand how variables and whitespace interact with PHP's language constructs.

In my (admittedly short) time golfing, I have found that PHP's language constructs (e.g. echo, return, for, while, etc) behave in a less-than-intuitive way when interacting with variables and whitespace.

echo$v;, for example, is perfectly valid, as are return$v; and other similar constructs. These small reductions in whitespace can lead to a significant cumulative decrease in length.

Keep in mind, though, that variables before language constructs require a space after, as in the following example:

foreach($a AS$b){}

Because AS is a language construct, a space is not required before the variable $b, but if one were to omit the space before it, resulting in $aAS, this would be parsed as a variable name and lead to a syntax error.

rintaun

Posted 2011-06-20T14:10:00.947

Reputation: 751

3foreach($a[1]as$b) needs no white space. This is not about language constructs and variables, but about spaces between word-characters of different words. – Titus – 2016-07-22T19:05:52.300

1Another instance where you need whitespace is in string concatenation. For instance, echo $a+5." text" will not work because PHP thinks the . is a decimal point for the 5. To make it work, you would need to add a space like this: echo $a+5 ." text" – Business Cat – 2016-08-16T14:47:22.063

@BasicSunset That statement can be written as echo$a+5," text";. The echo construct allows you to pass multiple parameters. where one would have to write echo"result: ".($a+5)."!";, you can write echo"result: ",$a+5,"!";. In fact, passing multiple parameters to an echo is a micro-optimization, since the code will run a tiny bit faster (since you don't concatenate the output, but send it separately). For challenges about writting the fastest code, this may help a tiny tiny tiny bit. – Ismael Miguel – 2017-04-07T07:59:14.560

@IsmaelMiguel It works with echo, but not with print (which you need if you put it inside an expression: echo is a pure construct with no return value while print can act as a function: it requires no parentheses, but it always returns int(1). – Titus – 2017-10-10T08:00:05.673

@Titus I didn't said anything about print. – Ismael Miguel – 2017-10-11T13:49:59.693

22

Use strings wisely.

This answer is two-fold. The first part is that when declaring strings, you can utilize PHP's implicit conversion of unknown constants to strings to save space, e.g:

@$s=string;

The @ is necessary to override the warnings this will produce. Overall, you end up with a one-character reduction.

is that sometimes, it may be space effective to set a variable to the name of an often used function. Normally, you might have:

preg_match(..);preg_match(..);

But when golfing, this can be shortened easily to:

@$p=preg_match;$p(..);$p(..);

With only two instances of "preg_match", you're only saving a single character, but the more you use a function, the more space you will save.

rintaun

Posted 2011-06-20T14:10:00.947

Reputation: 751

10@ is not needed in codegolf; notices and warnings (including E_DEPRECATED) are acceptable – Titus – 2016-07-22T19:13:20.653

3@Titus But in PHP, the warnings would output to the standard file output, so they are needed. – brianush1 – 2016-12-01T02:16:05.057

1@Titus I believe you can suppress them in the php.ini file – Stan Strum – 2018-03-06T15:24:29.817

12

You don't always need to write out conditional checks. For example, some frameworks use this at the top of their files to block access:

<?php defined('BASE_PATH')||die('not allowed');

Or in normal functions

$value && run_this();

instead of

if($value) { run_this(); }

Xeoncross

Posted 2011-06-20T14:10:00.947

Reputation: 379

It works also in JS – Евгений Новиков – 2017-07-08T17:14:30.967

8

Use short array syntax

Since PHP 5.4, arrays can be declared using square brackets (just like JavaScript) instead of the array() function:

$arr=['foo','bar','baz'];
// instead of
$arr=array('foo','bar','baz');

It will save five bytes.


But It may cost bytes if you have "holes" in an associative array:

$arr=array(,1,,3,,5);
// is one byte shorter than
$arr=[1=>1,3=>3,5=>5];

the disadvantage hits a little later if you can fill the holes with "empty" values:

$arr=[0,1,0,3,0,5,0,7,0,9,10,11];
// costs two byte more than
$arr=array(,1,,3,,5,,7,,9,,11);

rink.attendant.6

Posted 2011-06-20T14:10:00.947

Reputation: 2 776

2PHP 7.1 also introduced short list assignment: [,$a,$b,$c]=$argv;. – Titus – 2017-05-19T00:12:24.003

7

Use ${0}, ${1}, ${2}, ... instead of $a[0], $a[1], $a[2], ...

Unless you're performing an array manipulation, most references to an array index $a[$i] can be replaced with simply $$i. This is even true if the index is an integer, as integers are valid variable names in PHP (although literals will require brackets, e.g. ${0}).

Consider the following implementation of the Rabonowitz Wagon spigot:

3.<?for(;$g?$d=0|($a[$g]=$d*$g--/2+($a[$g]?:2)%$g*1e4)/$g--:238<<printf($e?'%04d':'',$e+$d/$g=1e4)^$e=$d%$g;);

This can be improved by 6 bytes, simply by replacing both array references $a[$g] with $$g instead:

3.<?for(;$g?$d=0|($$g=$d*$g--/2+($$g?:2)%$g*1e4)/$g--:238<<printf($e?'%04d':'',$e+$d/$g=1e4)^$e=$d%$g;);

primo

Posted 2011-06-20T14:10:00.947

Reputation: 30 891

1

I just saved 3 bytes with that: showcase.

– Titus – 2017-01-06T00:54:55.773

6

Running functions inside strings.

Try this:

$a='strlen';
echo "This text has {$a('15')} chars";

Or try this:

//only php>=5.3
$if=function($c,$t,$f){return$c?$t:$f;};
echo <<<HEREDOCS
    Heredocs can{$if(true,' be','not be')} used too and can{$if(<<<BE
{$if(true,1,0)}
BE
,'','not')} be nested
HEREDOCS;
//Expected output: Heredocs can be used too and can be nested

This only works with strings using "" and heredocs (DON'T make confusion with nowdocs).

Using nested functions is only possible inside nested heredocs (or you will run into parse errors)!

Ismael Miguel

Posted 2011-06-20T14:10:00.947

Reputation: 6 797

you will run into parse errors I cant read it myself? How does the pesky Zend engine put this together – Stan Strum – 2018-03-06T19:45:24.547

The next time I'm in a "PHP is a good programming language" argument, I'm going to use this as a counter-point. Wow. – primo – 2018-04-26T09:11:34.100

@primo Is it that bad? :O – Ismael Miguel – 2018-04-27T11:01:30.813

6

Learn a large subset of the library functions.

PHP's library is pretty huge and provides a ton of convenient functions that can greatly shorten various tasks. You could just search every time you try to do something, but beyond wasting time you might not find anything that matches your particular search. The best way is just to get familiar with the library and memorize function names and what they do.

Matthew Read

Posted 2011-06-20T14:10:00.947

Reputation: 521

6That's a lot of memorization, especially given the rather inconsistent naming of a whole lot of functions ;-) – Joey – 2011-06-24T20:49:01.220

@Joey Agreed. Akin to memorizing the Java library, except that would be arguably less useful since it's more verbose. – Matthew Read – 2011-06-24T20:54:52.013

3I find that the most important functions for the challenges I've come across so far here are the string manipulation and array manipulation functions. Creative use of those can really cut down the code. – migimaru – 2011-08-12T17:03:39.393

5

fun with typecasts

  • !!$foo will turn any truthy value to true (or 1 in output), falsy values (0, empty string, empty array) to false (or empty output)
    This will rarely be needed in code golf, for in most cases where you need a boolean, there is an implicit cast anyway.

  • (int)$foo can be written as $foo|0 or foo^0, but may need parentheses.
    For booleans and strings, $foo*1 or +$foo can be used to cast to int.

  • Unlike most other languages, PHP handles strings with numeric values as numbers. So if you have any string that contains a number you have to calculate with, just calculate.
  • The other way does not work: To multiply any number in a variable with 10, you could append a zero: *10 -> .0. But in this case, PHP will take the dot as decimal point and complain. (It´s different though if you have a variable amount of zeroes in a string.)
  • To turn an array into a string, use join instead of implode.
    If you don´t need a delimiter, don´t use it: join($a) does the same as join('',$a)
  • Incrementing strings: The most amazing feature imo is that $s=a;$s++; produces $s=b;. This works with uppercase and lowercase characters. $s=Z;$s++; results in $s=AA;.
    This also works with mixed case: aZ to bA, A1 to A2, A9 to B0 and z99Z to aa00A.
    Decrement does not work on strings. (And it does not on NULL).
    Back in PHP 3, $n="001";$n++; produced $n="002";. I am a little sad they removed that.

Whatever you golf: always have the operator precedence table at hand.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

4

looping through strings

can be done with 26 bytes or with 24 down to 18:

foreach(str_split($s)as$c)  # A) 26 - general
for($p=0;a&$c=$s[$p++];)    # B) 24 - general
for($p=0;$c=$s[$p++];)      # C) 22 - if $s has no `0` character
for(;a&$c=$s[$p++];)        # D) 20 - if $p is already NULL or 0 (does NOT work for false)
for(;$c=$s[$p++];)          # E) 18 - both C and D

for(;$o=ord($s[$p++]);)     # F) 23 - work on ASCII codes, if $s has no NULL byte and D
for(;~$c=$s[$p++];)         # G) 19 - if $s has no chr(207) and D

$a&$b does a bitwise AND on the (ascii codes of) the characters in $a and $b
and results in a string that has the same length as the shorter of $a and $b.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

can you please add ord($s[$p++]) as alternative for(;$s+=ord($argv[++$i])%32?:die($s==100);); against for(;$c=$argv[++$i];)$s+=ord($c)%32;echo$s==100; in this question https://codegolf.stackexchange.com/questions/116933/bernardino-identifies-unaltered-dollar-words

– Jörg Hülsermann – 2017-04-19T10:41:55.347

Please add ~ for cases you are working only with digits – Jörg Hülsermann – 2017-04-30T21:13:20.693

Note that PHP 7.2 yields warnings for the ~$c approach. – Titus – 2019-02-22T15:13:40.897

4

Use shorttags

In normal code, it's good practice to use <?php and ?>. However, this is not normal code - you are writing a code golf code. Instead of <?php, write <?. Instead of <?php echo, write <?=. Don't type ?> at end - it's completely optional. If you need ?> for some reason (for example to output text, and it's shorter somehow, or something, don't put a semicolon before it - it's not needed, as ?> implies semicolon.

Wrong (definitely too long):

<?php echo ucfirst(trim(fgets(STDIN)));?>s!

Correct:

<?=ucfirst(trim(fgets(STDIN)))?>s!

Konrad Borowski

Posted 2011-06-20T14:10:00.947

Reputation: 11 185

With the -r flag (which comes free), you don´t even any tags at all (and you´re not allowed to use any).

– Titus – 2019-02-22T15:12:17.030

4

Use ternary operators

if(a==2){some code;}else{some other code;}

can be abbreviated to this:

(a==2?some code:some other code);

Shorter, huh?

Chris vCB

Posted 2011-06-20T14:10:00.947

Reputation: 141

3The ternary operator has a weird behaviour in PHP, if you nest it. a?aa:ab?aba:abb:b evaluates to (a?aa:ab)?(aba):(abb) or something like that. – Titus – 2016-07-22T19:52:44.707

1And starting with PHP 5.3, you can omit the second operator: $a?:$b is the same as $a?$a:$b. – Titus – 2016-07-22T19:55:46.053

@Titus isn't that just ||? – Cyoce – 2017-01-05T22:25:34.427

1@Cyoce || casts to boolean in PHP. – Titus – 2017-01-05T23:29:22.803

a-2?some other code:some code. also for all languages. – Titus – 2017-01-17T02:43:14.537

“Conditional shorthands”? Better tell its real name, so those interested in more details can find it in the documentation: ternary operator.

– manatwork – 2014-01-02T17:53:11.420

Thanks, corrected. In informal use, I have heard it referred to mainly as 'conditional shorthands' or 'shorthand conditionals'. – Chris vCB – 2014-01-02T18:05:36.383

And with php 7+ you have access to ?? to check if isset. Example $a??$a:$b will evaluate to isset($a)?$a:$b. It's called the Null Coalescing Operator

– soenguy – 2019-03-19T12:36:32.530

3

The question asks for tips which are somewhat specific to PHP. This is one included in the tips for all languages.

– Peter Taylor – 2014-03-17T20:35:01.653

3

by any other name ... function aliases

use ...

  • join instead of implode
  • chop instead of rtrim (chop in PERL is different!)
  • die instead of exit
  • fputs instead of fwrite
  • is_int instead of is_integer or is_long
  • is_real instead of is_float or is_double
  • key_exists instead of array_key_exists
  • mysql instead of mysql_db_query

... to name the most important aliases. Take a look at http://php.net/aliases for more.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

Oh ... and did You know that die works with and without parameters? die(1) will exit the program with error code 1 (not completely sure on this; needs testing); die will exit with code 0, and die("Hello") will exit with code 0 after printing Hello. – Titus – 2017-03-09T17:04:32.840

3

Associative arrays can be merged with the + operator.

Instead of:

$merged = array_merge($a, $b);

Use:

$merged = $a + $b;

Note the + operator works with indexed arrays as well, but probably doesn't do what you want.

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

Indeed, frequently a good replacement, though not exactly the same: http://pastebin.com/seYeaP38

– manatwork – 2017-01-05T19:46:59.870

Ah yeah, dang it, I originally had the title "associative arrays ... " and then removed it. I'll clarify, thanks. – Alex Howansky – 2017-01-05T19:56:52.683

numeric arrays can also merged using +, as long as the indexes are distinct. If they are not, the values from the first array will be overwritten with those from the second one (just like array_merge). The difference: + does not reorder indexes. – Titus – 2017-01-06T00:11:29.190

3

some interesting facts on variable variables

I just had to share them (even before I verified that at least one of them helps golfing):

  • Use letters: $x=a;$$x=1;$x++;$$x=2;echo"$a,$b"; prints 1,2
    but other arithmetic operations do not work with letters.
  • As primo mentioned earlier, you can use pure numbers as variable names:
    $a=1;$$a=5;$a++;$$a=4;${++$a}=3;echo${1},${2},${3}; prints 543.
  • You can not only use [0-9a-zA-Z_] for variable names, but EVERY string:
    $x="Hello!";$$x="Goodbye.";echo${"Hello!"}; prints Goodbye..
  • But: Everything but [a-zA-Z_][a-zA-Z_0-9]* as variable names requires braces for literal use.
  • With no variables defined, $$x=1 sets ${NULL}, which is the same as ${false} and ${""}.
  • $a=1;$$a=5; does not only set ${1}, but also ${true}.

  • one more, the weirdest one I´ve found so far: Try $a=[];$$a=3;echo${[]};. Yes, it prints 3!

The reason for most of this: variable names are always evaluated to strings.
(Thanks @Christoph for pointing out.)
So, whatever you get when you print or echo the expression, that´s what you get as variable name.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

1Variable names are converted to strings that explains the last three points on your list. [] converts to Array: ${[]} = 5;echo $Array; prints 5. I'm pretty sure you know that but it might not be obvious to everyone :) – Christoph – 2017-03-15T09:16:28.573

@Jeff I fixed the typo. Thanks for noticing. – Titus – 2017-10-11T11:44:40.983

3

array_flip vs array_search

use

array_flip($array)[$value]

instead of

array_search($value,$array)

to save 1 Byte in arrays where the occurence of each value is unique

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

2

Arrow functions in PHP 7.4

PHP 7.4 is on RC2 version now and hopefully will be released in about 2 months. List of new features are here (this page can actually be updated when 7.4 is released). In 7.4, finally PHP has got the arrow functions, so not only function answers can be shorter now, but also passing closures to other functions can be a lot shorter too. Here are a few examples:

Return input + 1:

Anonymous function (closure) - 25 bytes - Try it online!

function($n){return$n+1;}

Arrow function - 12 bytes - Try it online!

fn($n)=>$n+1

Multiply items of first input (array of ints) by second input (int):

Anonymous function (closure) - 72 bytes - Try it online!

function($a,$n){return array_map(function($b)use($n){return$b*$n;},$a);}

Arrow function - 38 bytes - Try it online!

fn($a,$n)=>array_map(fn($b)=>$b*$n,$a)

Did you notice that $n is accessible in the inner function without a use $n statement? Yeah that is one of the arrow function features.


As a side note, I could not get arrow functions to work recursively (call the same arrow function inside itself), because we cannot give them a name and storing them as a closure in a variable like $f doesn't make $f accessible withing itself (sad). So this example doesn't work and using $f in first line causes a fatal error:

$f=fn($n)=>$n?$f($n-1):0;
$f(5); // Causes error: "PHP Notice: Undefined variable: f" + "PHP Fatal error: Uncaught Error: Function name must be a string"

But calling an arrow function withing a different arrow function works:

$f1=fn($n)=>$n+1;
$f2=fn($n)=>$f1($n-1);
$f1(2) // Returns 3
$f2(2) // Returns 2

Night2

Posted 2011-06-20T14:10:00.947

Reputation: 5 484

What if instead of $f=fn($n)=>$n?$f($n-1):0; you do $f=$F=fn($n)=>$n?$F($n-1):0;? Would that work? And then you call $(5) as usual. – Ismael Miguel – 2019-10-03T18:01:31.570

@IsmaelMiguel it still seems to throw same error. You can actually try on http://tio.run#php yourself as Dennis has updated its PHP to 7.4 RC2 a while back.

– Night2 – 2019-10-03T19:20:23.760

Can't get it to work. Seems that only variables defined before are available. – Ismael Miguel – 2019-10-04T08:34:28.783

2

avoid quotes where possible

PHP implicitly casts unknown words to literal strings.

$foo=foo; is the same as $foo='foo'; (assuming that foo is neither a key word or a defined constant): $foo=echo; does not work.

BUT: $p=str_pad; does; and $p(ab,3,c) evaluates to abc.

Using string literals without quotes will yield a Notice for Use of undefined constant; but that won´t show if you use the default value for error_reporting (CLI parameter -n).

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

I just noticed: This answer is a somewhat extended/updated duplicate of http://codegolf.stackexchange.com/a/2916/55735.

– Titus – 2017-01-17T02:49:44.387

Note: PHP before 7.2 yielded Notices (which you can opress with the -n flag); 7.2 yields Warnings; later versions will throw Errors! – Titus – 2019-02-22T15:17:54.703

2

line breaks
if the output requires line breaks, use a physical line break (1 byte) instead of "\n"
This also gives you a possible benefit to chose between single and double quotes.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

1

Directly dereference arrays returned from functions.

E.g., instead of this:

$a = foo();
echo $a[$n];

You can do:

echo foo()[$n];

This works with methods too:

echo $obj->foo()[$n];

You can also directly dereference array declarations:

echo [1, 2, 3, 4, 5][$n];

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

1

Use end() instead of array_pop()

The end() function doesn't just move the internal pointer to the end of the array, it also returns the last value. Note of course that it doesn't remove that value, so if you don't care what the array contains afterwards, you can use it instead of array_pop().

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

1

array_merge vs array_push

array_push($a,...$b); 

is one byte shorter than

$a=array_merge($a,$b);

Does not work the same with Associative arrays

variable-arg-list PHP >5.6

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

1

double array_flip vs in_array vs array_unique

in this special case a double array_flip saves 10 Bytes

($f=array_flip)($k=$f($c))) remove all double values in the array and I have dropped this $c=[], , |in_array($o,$c) and replace array_keys($c) with $k

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)
$x[$i]==$o?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,($f=array_flip)($k=$f($c)))==$y # boolean replacement string 1 equal to string 2
    ?join($k)." ".join($c) # output for true cases
:0; #Output false cases

Online Version

against

for($c=[],[,$x,$y]=$argv;a&$o=$y[$i];$i++)
  $x[$i]==$o|in_array($o,$c)?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,$c)==$y # boolean replacement string 1 equal to string 2
  ?join(array_keys($c))." ".join($c) # output for true cases
  :0; #Output false cases

Online version

against array_unique it saves 2 Bytes

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)
  $x[$i]==$o?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,array_unique($c))==$y # boolean replacement string 1 equal to string 2
  ?join(array_keys($c))." ".join($c) # output for true cases
  :0; #Output false cases

Online Version

After finding a bug in this program and replacement $x[$i]==$o?:$c[$x[$i]]=$o to ($p=$x[$i])==$o?:$k[$c[$p]=$o]=$p the double array_flip was not necessary longer

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

associative safe array_unique. Yay! – Titus – 2017-04-19T11:03:00.503

@Titus I have add your suggestion – Jörg Hülsermann – 2017-04-19T13:49:20.927

1

intersecting strings

Have you ever used
join("DELIMITER",str_split($s)) (31 bytes) or even
preg_replace(".","DELIMITER",$s) (32 bytes)
?

There´s a builtin for that:

Try chunk_split($s,1,"DELIMITER") (29 bytes).


If you omit the third parameter, chunk_split will use \r\n; that can save you 7 or 8 bytes.

But beware: chunk_split also appends the delimiter to the string,
so you may not get exactly what you want.

(If you don´t provide the chunk length, it will use 76. Rather unusual for code golf, but who knows.)

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

Maybe you should add an example in combination with strtr I love this idea. – Jörg Hülsermann – 2017-05-19T00:41:59.877

1

Removing characters in a string

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

saves 1 character compared to

str_replace(" ","",$string);

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

Note that this works for all (nonempty) strings, not just characters. – CalculatorFeline – 2017-06-23T16:58:11.860

@CalculatorFeline Why should it not work for empty strings? It make no sense or this case. – Jörg Hülsermann – 2017-06-23T17:02:59.547

Well, the first version doesn't work with "" and it's not very useful anyway. – CalculatorFeline – 2017-06-23T17:04:16.503

@CalculatorFeline Could you explain why it should not work? Try it online!

– Jörg Hülsermann – 2017-06-23T17:07:05.727

I meant " "="". Try it online!

– CalculatorFeline – 2017-06-23T17:11:24.380

1@CalculatorFeline And for this case a zero byte solution is much better. It make no sense to do this in that way. – Jörg Hülsermann – 2017-06-23T17:17:55.890

3Your join example is missing a ). And strtr($string,[" "=>""]) is even shorter. – Titus – 2017-10-10T08:23:31.457

1

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

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

1

unset() vs INF

In a case search for a minimum in an array you can use instead of

unset($var[$k]);

$var[$k]=INF;

to save 3 Bytes

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

1

Combining for loops

Suppose you have code of the following form:

for($pre1; $cond1; $post1) for($pre2; $cond2; $post2) $code;

this can generally be re-rolled in the following form:

for($pre1; $cond2 • $post2 || $cond1 • $pre2 • $post1; ) $code;

where represents a generic combining operator. This usually results in an byte count reduction, but will likely require some creativity. $cond2 will need to be written so that it fails the first time through. $post1 should also fail to execute the first time, although it may be easier to refactor beforehand so that $post1 is not present.

If you're working with three or more nested loops, you can also combine two first, and then combine that to another, and so on. I find that it has generally been easier to combine from the inside outwards.


As an example, consider the following solution to the H-carpet fractal (97 bytes):

for(;$i<$n=3**$argn;$i+=print"$s\n")for($s=H,$e=1;$e<$n;$e*=3)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

This can be reformulated in the following way:

for(;($i+=$e&&print"$s\n")<$n=3**$argn;)for($s=H,$e=1;$e<$n;$e*=3)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

$e&&print prevents print on first iteration, and also does not increment $i.

and finally (93 bytes):

for(;$H>$e*=3or$e=($i+=$e&&print"$s\n")<${$s=H}=3**$argn;)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

$H>$e*=3 will fail the first time as both variables are undefined.

primo

Posted 2011-06-20T14:10:00.947

Reputation: 30 891

1

Use boolean operators instead of strtoupper() and strtolower()

If you're working exclusively with strings consisting of alphabet characters, you can use boolean operators to change them to uppercase or lowercase with fewer keystrokes than PHP's built-in functions.

Example:

// Convert lowercase to uppercase
$s = "g";
echo strtoupper($s);  // Outputs 'G', uses 20 characters
echo~" "&$s;          // Outputs 'G', uses 12 characters

// Convert uppercase to lowercase
$s = "G";
echo strtolower($s);  // Outputs 'g', uses 20 characters
echo$s^" ";           // Outputs 'g', uses 11 characters

// Switch case of each character
$s = "Gg";
echo$s^"  ";          // Outputs 'gG', uses 12 characters

Things are a little trickier for strings of arbitrary length, but the & and ^ operators will truncate the result to the length of the shorter input string. So for example, if $W is a string of spaces at least as long as any input $s, then ~$W&$s is equivalent to strtoupper($s), and $s|$W^$s is equivalent to strtolower($s) (whereas $s|$W by itself will produce a string with additional spaces unless $s and $W are of equal length).

r3mainer

Posted 2011-06-20T14:10:00.947

Reputation: 19 135

1

Regarding file I/O:

Linking to another related question, the answers to which fit here.

Gaffi

Posted 2011-06-20T14:10:00.947

Reputation: 3 411

0

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;

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

With a rare exception: when you need to append more than 3 elements, array_push($array, $value, $value, $value, $value); may be shorter; – manatwork – 2017-01-05T19:37:57.357

Heh yeah, also note, if $value is itself an array, it's probably not going to do what you want. – Alex Howansky – 2017-01-05T19:42:06.750

0

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);

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

0

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('...');

Alex Howansky

Posted 2011-06-20T14:10:00.947

Reputation: 1 183

1I think explode is also worth a shot. – Titus – 2017-01-06T00:08:48.087

0

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]

Jörg Hülsermann

Posted 2011-06-20T14:10:00.947

Reputation: 13 026

0

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.

jimmy23013

Posted 2011-06-20T14:10:00.947

Reputation: 34 042

0

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..

ArtisticPhoenix

Posted 2011-06-20T14:10:00.947

Reputation: 329

0

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.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

primo also posted on combining loops in March 2014. Don´t miss out on that one!

– Titus – 2019-02-22T15:26:01.957

0

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)

mickmackusa

Posted 2011-06-20T14:10:00.947

Reputation: 121

0

You may not always require echo, for instance:

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

Vs

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

Shaun Bebbers

Posted 2011-06-20T14:10:00.947

Reputation: 1 814

0

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.

Titus

Posted 2011-06-20T14:10:00.947

Reputation: 13 814

2Watch out! These were removed in PHP 7.0. – Umbrella – 2018-09-07T18:34:19.477