Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Upgrading Legacy to the Latest PHP Version

Upgrading Legacy to the Latest PHP Version

Your application runs on an old PHP version that is about to go out of support. How to upgrade without breaking the application? You will need the right tools for detecting and fixing those issues, as well as a way to verify that the application is still working correctly. In this presentation, I will share the strategy that I employed when upgrading millions of lines of untested code.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina
PRO

October 28, 2021
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. Upgrading Legacy to the Latest PHP Version IPC MUNICH |

    OCT 28, 2021 @afilina
  2. "I don't know whether my application works on the latest

    PHP."
  3. • Check compatibility. • Fix issues. • Test the fixes.

  4. Anna Filina • Coding since 1997 (VB4) • PHP since

    2003 • Legacy archaeology • Test automation • Public speaking • Mentorship • YouTube videos
  5. Docker image PHPCO PHPCompatibility

  6. phpco() { docker run --init -v $PWD:/mnt/src:cached --rm -u "$(id

    -u):$(id -g)" frbit/phpco:latest $@; return $?; }
  7. phpco -p --colors --extensions=php --runtime-set testVersion 7.4 .

  8. FILE: /mnt/src/app/controllers/MyController.php ------------------------------------------------------------------ FOUND 1 ERROR AFFECTING 1 LINE ------------------------------------------------------------------

    166 | ERROR | Using 'break' outside of a loop or switch structure | | is invalid and will throw a fatal error since PHP | | 7.0 ------------------------------------------------------------------
  9. public function myFunction($result) { if ($result == null) { break;

    } }
  10. public function myFunction($result) { if ($result == null) { return;

    } }
  11. PHPCompatibility tells us what to fix.

  12. Fixing

  13. $response = '1234567890'; $header = 'Content-length: ' . strlen($response) +

    1; echo $header;
  14. 3v4l.org

  15. None
  16. None
  17. None
  18. $response = '1234567890'; $header = 'Content-length: ' . (strlen($response) +

    1); echo $header;
  19. Function each() is deprecated since PHP 7.2; Use a foreach

    loop instead
  20. while (list($i, $row) = each($result)) foreach ($result as $i =>

    $row)
  21. $i = each($result)['key']; $v = each($result)['value']; $i = each($result)[0]; $v

    = each($result)[1];
  22. $a = each($result); $b = each($result);

  23. else if (list(, $optArg) = each($args))

  24. final class Php56Compatibility { public static function each(array &$array) {

    if (current($array) === false) { return false; } $key = key($array); $value = current($array); next($array); return [ 1 => $value, 'value' => $value, 0 => $key, 'key' => $key, ]; } }
  25. Portable, unit-tested solution wrapped in a class.

  26. We can clean things up at a later time (technical

    debt).
  27. mcrypt PHP

  28. mcrypt PECL PHP

  29. mcrypt PECL PHP libmcrypt

  30. PHP openssl

  31. mcrypt.OFB != openssl.OFB

  32. How to test mcrypt to openssl upgrade?

  33. • Can we decrypt what we just encrypted? • Can

    we decrypt what we already have in our database?
  34. encrypted App with mcrypt original

  35. public function testCanDecrypt() { //... $decrypted = $security->decrypt($encrypted); self::assertEquals($original, $decrypted);

    }
  36. mysql_ mysqli_ PDO

  37. • Connection object needs to be available. • Connection object

    now a mysqli type. • Field metadata represented differently. • Different methods for unbuffered queries.
  38. mysql to mysqli conversion can't be unit-tested.

  39. mysql_connect mysql_query mysql_close mysqli_connect mysqli_query mysqli_close $connection

  40. /login redirect to profile credentials session database encryption

  41. /login redirect to profile credentials POST /login Redirect URL

  42. $response = $this->guzzle->send($request); self::assertEquals(/**/);

  43. You can use this approach to test the entire application.

  44. PHPCO won't detect everything.

  45. <?php class MyClass extends BaseClass { public function validate($field, $value)

    { //... } } class BaseClass { public function validate(string $field, $value, $message = null) { //... } }
  46. None
  47. • Start testing. • Note the errors that appear. •

    Tune static analysis for those specific errors.
  48. Write more tests.

  49. var_dump(0 == "");

  50. None
  51. What about the framework?

  52. • New framework might be too different. • Is there

    a fork that keeps up with PHP?
  53. You could make the framework compatible.

  54. What about the tests?

  55. • Is PHPUnit compatible with PHP? • Can it be

    updated? • Might need to upgrade PHPUnit.
  56. Do not fix your tests and code at the same

    time.
  57. /login redirect to profile credentials POST /login Redirect URL

  58. Tests PHP 8.0 PHP 5.4 Application

  59. Tests PHP 8.0 PHP 8.0 Application

  60. • Use PHPCompatibility • Use Psalm, PHPStan, etc. • Don't

    be afraid to fix the framework/libs. • Write tests.
  61. @afilina Questions?