## DEV Community is a community of 555,288 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

loading...

# PHP: Return true to win - WriteUp (Part 1)

Antony Garand ・7 min read

# Introduction

Returntrue.win is a website containing 16 PHP challenges where we must return true using the least amount of characters in the given context.

The challenges demonstrate many interesting quirks from PHP and are of greatly varying difficulty, which is why solving and understanding those can help us gain a better understanding of the language.

This post will cover the first 8 solutions, while an upcoming part 2 will cover the remaining ones.

Now is the time to stop reading and try to solve the challenges yourself before reading on the solutions!

Website: https://returntrue.win

## Level 1

#### Challenge

``````function foo(\$x)
{
return \$x;
}
``````

#### Solution

``````foo(!0);
``````

#### Explanation

The obvious solution would be to enter `true`, but that would be 4 bytes and the shortest answer uses only half of those! We can save two bytes by using `!0` instead.
The reason `!0` works is as the logical not (!) operator will type cast the value to a boolean first, and return the opposite of this result.

As `0` is converted to `false`, we get `!false`, which is `true`.

See: Boolean type casting for a list of values converted to false.

## Level 2

#### Challenge

``````function foo(\$x)
{
return \$x('gehr') === 'true';
}
``````

#### Solution

``````foo(str_rot13)
``````

#### Explanation

Our first guess could be to create an anonymous function which always returns `'true'`, such as the following:

``````foo(function(){return'true';})
``````

But this isn't the shortest solution.

The key to the shortest 9 bytes score is the 'gehr' argument given to the function, which is exactly `true` with each letter shifted by 13 characters.

As it turns out, php has the str_rot13 function which performs this exact operation!

PHP doesn't like crashing, and it does so by trying to be smart with its conversions.
When we're using a constant which is not defined, the following behavior is used:

If you use an undefined constant, PHP assumes that you mean the name of the constant itself, just as if you called it as a string (CONSTANT vs "CONSTANT").
From: PHP Constant syntax

Combine this with Variable Functions

This means that if a variable name has parentheses appended to it, PHP will look for a function with the same name as whatever the variable evaluates to, and will attempt to execute it.

And we've got a working solution!

## Level 3

#### Challenge

``````function foo(\$x)
{
return (\$x >= 1) && (\$x <= 1) && (\$x !== 1);
}
``````

#### Solution

``````foo(!0)
// Or
foo(1.)
``````

#### Explanation

The first requirement here is get a variable which is `<=` and `>=` than 1.
As those are numeric comparisons, PHP will convert both sides of the equation to numbers.

The second requirement is for our variable to not be the integer `1`.

Booleans converted to numbers will manage to pass this condition:

FALSE will yield 0 (zero), and TRUE will yield 1 (one).

From the doc: Integer: Casting from boolean

This gives us our first answer, passing `true` will succeed in all the checks.

The second solution is based on PHP's handling of its different numeric types.

As you may know, PHP has both floats and integers.

As they are different types, `1.0` and `1` returns true to the Not identical operator.

Our solution is therefore the float value of `1`, which can be written `1.`

## Level 4

#### Challenge

``````function foo(\$x)
{
\$a = 0;
switch (\$a) {
case \$x:
return true;
case 0:
return false;
}
return false;
}
``````

#### Solution

``````foo(0);
``````

#### Explanation

As `\$a` is `0` an we need the program flow to enter our `case \$x`, the solution is to set `\$x` to `0`.

Not much of a challenge here!

## Level 5

#### Challenge

``````function foo(\$x)
{
return strlen(\$x) === 4;
}
``````

#### Solution

``````foo(💩);
``````

#### Explanation

The tricky part here is getting the shortest solution, which is only 1 character long yet `strlen` will return 4.
The solution resides in the difference between `strlen` and `mb_strlen`, which handles multibyte strings differently:

strlen() returns the number of bytes rather than the number of characters in a string.

[mb_strlen()] A multi-byte character is counted as 1.

As utf8 supports up to 4 bytes per character, our poop emoji returns `4` on the `strlen` function while being only one character.

References:
strlen documentation

mb_strlen documentation

## Level 6

#### Challenge

``````function foo(\$x)
{
return \$x === \$x();
}
``````

#### Solution

``````// Not working anymore, 22 characters
foo((\$x=session_id)(\$x).\$x);
// Shortest? working solution: 33 characters
foo(\$x=function()use(&\$x){return \$x;})
// Alternative working solution, 42 characters
foo(\$GLOBALS[x]=function(){return\$GLOBALS[x];})
// Runner up, 44 characters
foo(new class{function __invoke(){return\$this;}})
``````

#### Explanation

This one is among the most interesting challenges of the lot, and I've spent a lot of time finding the shortest solution.

Note that at the time of this writing, the shortest solution will NOT work due to additional sandboxing done on the website, but it does work locally if you have sessions enabled.

There are four different solutions which I'd like to explore here.

##### Shortest
``````(\$x=session_id)(\$x).\$x
// Which is equivalent to
\$x = 'session_id';
\$x(\$x);
foo(\$x);
``````

Firstly, the essence of this solution is the session_id function.

This function is used as an getter/setter:

• When called without an argument, it will return the stored value.
• When called with an argument, it will assign its inner value and return an empty string.

The first part of this solution is setting the `\$x` variable to `'session_id'` (As string, because of PHP's handling of undefined constants).

The second part is calling session_id, which works once again thanks to PHP's Variable functions, with `\$x` as argument. As `\$x` is a string, this will set the content of `session_id` to `'session_id'`.

The result of our `session_id(\$x)` call is an empty string, which isn't what we want to send to `foo`.

In order to send `session_id` to `foo`, we concatenate `\$x` to the empty string we previously had, which results in the `'session_id'` string.

Finally, the foo function will compare our entry variable `\$x`, aka. `'session_id'`, to the result of `\$x()`, aka. `session_id()`.
As we did set our `session_id` to `'session_id'`, both strings are the same and we passed the comparison!

##### Self returning function using use
``````\$x=function()use(&\$x){return \$x;}
``````

This solution is pretty straightforward: Create a function which uses itself, and returns itself.

Reference: Anonymous functions, specifically example #3

##### Globals
``````\$GLOBALS[x]=function(){return\$GLOBALS[x];}
// Which is equivalent to

\$GLOBALS[x] = function() {
return \$GLOBALS[x];
};
foo(\$GLOBALS[x]);
``````

This solution is similar to the previous one, but uses the \$GLOBALS array instead of inheriting variables from the parent scope.

##### Callable class returning itself
``````new class {
function __invoke()
{
return \$this;
}
}
``````

Since PHP7, we can create Anonymous Classes.

This lets us create a new class with the __invoke magic method implemented.

This magic method lets us return `\$this` when we call our object as a function to solve the challenge!

## Level 7

#### Challenge

``````function foo(stdClass \$x)
{
\$x = (array) \$x;
return \$x[0];
}
``````

#### Solution

``````foo((object)[!0]);
``````

#### Explanation

This solution resides in how PHP type casts to object.

As we are typecasting an array with `[0 => true]` to an object, we are creating a generic stdClass with the `0` property having the `true` value.

Once this is type casted back to an array with the `\$x = (array) \$x;` line, the property `0` of our object gets back to the new array's index 0.

## Level 8

#### Challenge

``````class Bar {}

function foo(Bar \$x)
{
return get_class(\$x) != 'Bar';
}
``````

#### Solution

``````foo(new class extends Bar{});
``````

#### Explanation

This solution depends on Object Inheritance in PHP.

We can create a new child class which extends `Bar` as an argument. As the given object is a new class, get-class will return the name of the new anonymous class and not 'Bar'.

# Conclusion

Those were lots of original solutions to the challenge!

Most of those solutions did produce warnings and notices, therefore most of the odd behavior should be detected in a proper development environment.

I would like to thank:

If you do know similar oddities or challenge websites, please do send them my way so I can solve and write about them!

# References

## Discussion

Comment deleted
Josh Cheek

You can't really figure out when user code is malicious, and escaping won't work here b/c the input itself must be evaluated. IDK how it's implemented, my guess is that it's running on a sandboxed server (eg has memory/processor/duration thresholds set, which will kill the program if you exceed them, has abusable features like http and system commands disabled). The comment about `session_id` would support this hypothesis. There are other options, though. I've done things like this by shipping them off to eval.in, which does its own sandboxing. You could also compile php to web assembly and run it in the user's browser (guessing this would take quite a bit of work, but it should be possible).

Antony Garand

This all depends on how you validate your user input!

This may not be a simple `eval`, but perhaps a docker container launched specifically for this test and destroyed afterwards.

I know that's how pwnfixrepe.at does evaluate untrusted code safely, and therefore there are most likely similar mechanisms in place here.

robencom

Hehe I like a PHP challenge when I see one, but when one of the answers is a POOP emoji, then you'll know that this challenge has passed the limits of logic :)

Ben Sinclair

I'm glad you explained this because I had no clue what I was supposed to do from the website itself!