PHP Golf Intermezzo #2 - de uitslagenDe opdracht van deze tussen-golf was het maken van een PHP-script dat als output het eigen script omvatte. Al snel werd het keyword 'quine' genoemd waardoor het niet zo moeilijk was om een codevoorbeeld te vinden, maar zoveel korte voorbeeldquines in PHP zijn er helaas niet... Gruwelijk veel mensen zijn hier mee bezig geweest maar uiteindelijk wisten slechts twee mensen de mailknop te vinden om toch nog binnen de tijd hun brouwsels op te sturen. We beginnen met de oplossing met het meest aantal tekens;
Crisp - 131 tekens:1 | <?$a='PD8kYT0nKic7ZWNobyBzdHJfcmVwbGFjZShjaHIoNDIpLCRhLGJhc2U2NF9kZWNvZGUoJGEpKTs=';echo str_replace(chr(42),$a,base64_decode($a)); |
Dit is een klassieker in de quine-wereld. De variabele $a bevat een base64 geëncoded stukje code die na een base64_decode() er zo uit ziet:
1 | <?$a='*';echo str_replace(chr(42),$a,base64_decode($a)); |
chr(42) is de benoeming voor de asterisk ('*') en dit tekentje wordt dan ook na de base64_decode() vervangen door de inhoud van variabele $a, de geëncodeerde code. Hierdoor kom je op exact dezelfde code uit als hetgeen je mee begint.
SuperRembo - 113 tekens:1 | <?$s="6563686F273C3F24733D22272E24732E27223B272E7061636B2822482A222C2473293B";echo'<?$s="'.$s.'";'.pack("H*",$s); |
SuperRembo maakt slim gebruik van de pack() functie die PHP heeft overgeërfd vanuit Perl. Het resultaat van de pack() functie zoals hij hierboven staat is dit:
1 | echo'<?$s="'.$s.'";'.pack("H*",$s); |
Dit zorgt ervoor dat na uitvoering net zoals bij de base64_decode() variant van Crisp de output exact hetzelfde is als het script.
Crisp - 63 tekens:1 | <?eval($c='echo\'<?eval($c=\\\'\'.addslashes($c).\'\\\');\';'); |
Een aanzienlijk kortere quine met een duidelijk andere aanpak. Crisp maakt gebruik van het feit dat een toewijzing aan een variabele ($c=...) de inhoud van de toewijzing zelf terug geeft en dus gebruikt kan worden als parameter van eval(). De inhoud van $c is echter een PHP-scriptje met een echo welke de inhoud van het script toont. Dit werkt doordat éérst $c wordt toegewezen tijdens het verwerken van de parameter van eval(), waarna vervolgens de eval daadwerkelijk wordt uitgevoerd. Een slimme aanpak die we straks nog terug zullen zien.
Crisp - ongeldig:1 | <?=printf($a='<?=printf($a=%c%s%c,39,$a,39);',39,$a,39); |
Het idee is goed, alleen vergeet Crisp hier dat printf() de lengte van de geoutputte string teruggeeft en dat de output in dit geval niet gelijk is aan het PHP-script zelf.
SuperRembo - 54 tekens:1 | <?printf($x='<?printf($x=%c%s%c,39,$x,39);',39,$x,39); |
Nu wordt het serieus, SuperRembo heeft in principe dezelfde quine als de voorgaande met het verschil dat deze foutloos is en dus geen '<?=' bevat. printf() kan worden gebruikt om een string te 'formatten' met variabelen. Dit houdt in dat je in de string zelf bepaalde codes opgeeft die je later laat vervangen door de inhoud van variabelen. printf() neemt als eerste argument de string die geformat dient te worden, en als volgende argumenten de variabelen die in die string geplaatst dienen te worden. De code '%c' staat voor 'character' en geeft in principe de waarde terug van chr($n). De code '%s' staat voor 'string' en plaatst op die plek het argument als een string.
Code 39 staat voor de apostrofe (') en $x wordt in de string geplaatst waardoor de output verdacht veel op het script zelf lijkt
![]()
hier is dit ook weer mogelijk aangezien in PHP de argumenten van links naar rechts worden verwerkt en $x dus éérst gevuld wordt voordat het in de string wordt gezet.
Crisp - 44 tekens:1 | <?eval($c='echo"<?eval(\$c=\x27$c\x27);";'); |
Crisp laat duidelijk zien waar hij toe in staat is en verbetert zijn eval()-quine door slim gebruik te maken van escaped characters in dubbel quoted strings. De '\x27' is een speciale manier om in dubbel quoted strings de apostrofe weer te geven waardoor er nu geen apostrofes meer hoeven te worden ge-escaped. Een addslashes() neemt vrij veel tekens in en dit is dan ook een slimme manier om aan de geweldige 44 tekens te komen.
Crisp, gefeliciteerd!