<!--
  canonical-url: https://www.avonture.be/blog/php-obfuscator
  published:     2024-04-09
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# Obfuscate your PHP code

> Protect your PHP code with obfuscation. Discover how the php_obfuscator script works and if AI tools like Copilot can still de-obfuscate your proprietary source code.

---
![Obfuscate your PHP code](/img/v2/obfuscated_code.webp)

**TL;DR**

This article tests whether PHP code obfuscation (using the author's `php_obfuscator` script, which strips whitespace and renames identifiers to random strings) still protects proprietary code in the AI era. A hangman game generated by Gemini is obfuscated and still runs correctly, but Copilot is then able to fully de-obfuscate it back into readable, commented code — questioning whether obfuscation is still worthwhile.

A few years ago, I sold a software called aeSecure, which was a Web Application Firewall. In order to prevent my proprietary code from being picked up by someone who could then sell it, for example, I had developed an obfuscation script.

Since then, I've released a reusable version of that script: <https://github.com/cavo789/php_obfuscator>.

And what if, today, with artificial intelligence, this type of code was no longer of any interest? The aim of this article is to see if it's always a good idea to make your code unreadable.

## Obtaining a previously non-existent code, ask Gemini

Just to give you some PHP code to use as an example for this article, let's create a hangman game using artificial intelligence.

![Asking Gemini](./images/gemini.webp)

Here is the code proposed by [Gemini](https://gemini.google.com/). Save it to your disk as `hangman.php`:

**hangman.php**

```php
<?php

function generateWordList() {
  // Replace these with your own word list
  return [
    "apple",
    "banana",
    "orange",
    "computer",
    "programming",
  ];
}

function getRandomWord($wordList) {
  $randomIndex = array_rand($wordList);
  return $wordList[$randomIndex];
}

function displayHangman($guessesLeft) {
  $stages = [
    "[   O    ]",
    "[   |    ]",
    "[  /|    ]",
    "[  /|\\   ]",
    "[   |    ]",
    "[  /     ]",
    "Wrong guess: ",
  ];
  echo $stages[$guessesLeft] . PHP_EOL;
}

function displayWord($word, $guessedLetters) {
  $displayedWord = "";
  for ($i = 0; $i < strlen($word); $i++) {
    $letter = $word[$i];
    if (in_array($letter, $guessedLetters)) {
      $displayedWord .= $letter . " ";
    } else {
      $displayedWord .= "_ ";
    }
  }
  echo trim($displayedWord) . PHP_EOL;
}

function playHangman() {
  $wordList = generateWordList();
  $word = getRandomWord($wordList);
  $guessedLetters = [];
  $guessesLeft = 6;

  while ($guessesLeft > 0) {
    displayHangman($guessesLeft);
    displayWord($word, $guessedLetters);

    echo "Guess a letter: ";
    $guess = trim(fgets(STDIN));

    if (strlen($guess) !== 1 || !ctype_alpha($guess)) {
      echo "Please enter a single letter." . PHP_EOL;
      continue;
    }

    $guess = strtolower($guess);

    if (in_array($guess, $guessedLetters)) {
      echo "You already guessed that letter." . PHP_EOL;
      continue;
    }

    $guessedLetters[] = $guess;

    if (strpos($word, $guess) === false) {
      $guessesLeft--;
    }

    if (str_replace(" ", "", displayWord($word, $guessedLetters)) === $word) {
      echo "Congratulations! You guessed the word." . PHP_EOL;
      break;
    }
  }

  if ($guessesLeft === 0) {
    displayHangman($guessesLeft);
    echo "You ran out of guesses. The word was: " . $word . PHP_EOL;
  }
}

playHangman();
```

To play with this game, just run `docker run -it -v ${PWD}:/src -w /src php:8.3-fpm php hangman.php` (see [The easiest way to run a PHP script / website](/blog/docker-php-run-script-or-website) if that command looks cryptic).

As you'll see the script works fine without any change!

![Playing](./images/playing.webp)

## Obfuscate

Now, we'll make this code unreadable by removing unneeded spaces and carriage returns, and replacing names with some random letters. For instance, replace the `playHangman` function name with a random string like `ppny`. And to make the code more unreadable, I'll choose only strings like `ppmx`, `ppnx`, `ppny` and so on.

To do such obfuscation, just download my <https://github.com/cavo789/php_obfuscator/blob/main/src/minify.php> script and save it on your disk as `minify.php`. Also create a file called `settings.json` with this content:

**settings.json**

```json
{
    "replace": {
        "getRandomWord":"ppmx",
        "displayHangman":"ppnx",
        "playHangman":"ppny",
        "displayWord":"ppnz",
        "guessesLeft":"ppnc",
        "guessedLetters":"ppnt",
        "randomIndex":"ppmw",
        "wordList":"ppmu",
        "generateWordList":"ppms",
        "displayedWord":"ppmb",
        "stages":"ppmc"
    }
}
```

Right now, in your folder, you've three files:

```bash
$ ls -alh
total 28K
drwxr-xr-x  2 root root 4.0K Apr  7 20:11 .
drwxrwxrwt 22 root root 4.0K Apr  7 19:55 ..
-rw-r--r--  1 root root 1.9K Apr  7 20:07 hangman.php
-rw-r--r--  1 root root  12K Apr  7 19:34 minify.php
-rw-r--r--  1 root root  731 Apr  7 19:47 settings.json
```

Time to make the `hangman.php` file unreadable by running `docker run -it -v ${PWD}:/src -w /src php:8.3-fpm php minify.php input=hangman.php output=hangman_minify.php`.

![Obfuscating your PHP code](./images/obfuscate.webp)

Now, you've a new file called `hangman_minify.php`:

**hangman\_minify.php**

```php
<?php
// cspell:disable
function ppms(){return [
"apple",
"banana",
"orange",
"computer",
"programming",
];}
function ppmx($ppmu){$ppmw=array_rand($ppmu);return $ppmu[$ppmw];}
function ppnx($ppnc){$ppmc=[
"[   O    ]",
"[   |    ]",
"[  /|    ]",
"[  /|\\   ]",
"[   |    ]",
"[  /     ]",
"Wrong guess: ",
];echo $ppmc[$ppnc].PHP_EOL;}
function ppnz($word,$ppnt){$ppmb="";
for ($i=0; $i < strlen($word); $i++){$letter=$word[$i];if(in_array($letter,$ppnt)){$ppmb .=$letter." ";}else{$ppmb .="_ ";}}echo trim($ppmb).PHP_EOL;}
function ppny(){$ppmu=ppms();$word=ppmx($ppmu);$ppnt=[];$ppnc=6;while ($ppnc > 0){
ppnx($ppnc);
ppnz($word,$ppnt);echo "Guess a letter: ";$guess=trim(fgets(STDIN));if(strlen($guess) !==1||!ctype_alpha($guess)){echo "Please enter a single letter.".PHP_EOL;continue;}$guess=strtolower($guess);if(in_array($guess,$ppnt)){echo "You already guessed that letter.".PHP_EOL;continue;}$ppnt[]=$guess;if(strpos($word,$guess)===false){$ppnc--;}
if(str_replace(" ", "", ppnz($word,$ppnt))===$word){echo "Congratulations! You guessed the word.".PHP_EOL;break;}}
if($ppnc===0){
ppnx($ppnc);echo "You ran out of guesses. The word was: ".$word.PHP_EOL;}}
ppny();
```

What you see here above is the newer version of the `hangman.php` file and it's still running. You can check by running `docker run -it -v ${PWD}:/src -w /src php:8.3-fpm php hangman_minify.php`.

## Make the code readable again, ask Copilot

![Asking copilot](./images/copilot.webp)

The first time, [Copilot](https://copilot.microsoft.com/) has kept my minified function name. By asking him *Can you suggest another version?*, Copilot suggests this code:

![Rewriting the code](./images/copilot_rewritting.webp)

**hangman.php**

```php
<?php
// Define an array of words
function getRandomWord()
{
    $words = [
        "apple",
        "banana",
        "orange",
        "computer",
        "programming",
    ];
    return $words[array_rand($words)];
}

// Display hangman art based on the number of incorrect guesses
function displayHangman($incorrectGuesses)
{
    $hangmanArt = [
        "[   O    ]",
        "[   |    ]",
        "[  /|    ]",
        "[  /|\\   ]",
        "[   |    ]",
        "[  /     ]",
    ];
    echo $hangmanArt[$incorrectGuesses] . PHP_EOL;
}

// Display the word with guessed letters revealed
function displayWord($word, $guessedLetters)
{
    $displayedWord = "";
    foreach (str_split($word) as $letter) {
        if (in_array($letter, $guessedLetters)) {
            $displayedWord .= $letter . " ";
        } else {
            $displayedWord .= "_ ";
        }
    }
    echo trim($displayedWord) . PHP_EOL;
}

// Main game loop
function playHangman()
{
    $wordToGuess = getRandomWord();
    $guessedLetters = [];
    $incorrectGuesses = 6;

    while ($incorrectGuesses > 0) {
        displayHangman($incorrectGuesses);
        displayWord($wordToGuess, $guessedLetters);
        echo "Guess a letter: ";
        $guess = trim(fgets(STDIN));

        if (strlen($guess) !== 1 || !ctype_alpha($guess)) {
            echo "Please enter a single letter." . PHP_EOL;
            continue;
        }

        $guess = strtolower($guess);

        if (in_array($guess, $guessedLetters)) {
            echo "You already guessed that letter." . PHP_EOL;
            continue;
        }

        $guessedLetters[] = $guess;

        if (strpos($wordToGuess, $guess) === false) {
            $incorrectGuesses--;
        }

        if (str_replace(" ", "", displayWord($wordToGuess, $guessedLetters)) === $wordToGuess) {
            echo "Congratulations! You guessed the word." . PHP_EOL;
            break;
        }
    }

    if ($incorrectGuesses === 0) {
        displayHangman($incorrectGuesses);
        echo "You ran out of guesses. The word was: " . $wordToGuess . PHP_EOL;
    }
}

// Start the game
playHangman();
```

The artificial intelligence managed to understand the purpose of the script (a hangman game) and rewrote the code to make it readable. Not only is the code readable, the function names are too, but it has also written comments. Just incredible!

Obfuscation aside, if you're after more conventional PHP code-quality tooling, check out [the Docker image bundling static analysis tools](/blog/php-jakzal-phpqa).
