$30 off During Our Annual Pro Sale. View Details »

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.

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

    View Slide

  2. "I don't know whether my application
    works on the latest PHP."

    View Slide

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

    View Slide

  4. Anna Filina
    • Coding since 1997 (VB4)
    • PHP since 2003
    • Legacy archaeology
    • Test automation
    • Public speaking
    • Mentorship
    • YouTube videos

    View Slide

  5. Docker image
    PHPCO
    PHPCompatibility

    View Slide

  6. phpco() { docker run --init -v $PWD:/mnt/src:cached --rm -u "$(id
    -u):$(id -g)" frbit/phpco:latest $@; return $?; }

    View Slide

  7. phpco -p --colors --extensions=php --runtime-set testVersion 7.4 .

    View Slide

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

    View Slide

  9. public function myFunction($result)
    {
    if ($result == null) {
    break;
    }
    }

    View Slide

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

    View Slide

  11. PHPCompatibility
    tells us what to fix.

    View Slide

  12. Fixing

    View Slide

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

    View Slide

  14. 3v4l.org

    View Slide

  15. View Slide

  16. View Slide

  17. View Slide

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

    View Slide

  19. Function each() is deprecated since PHP 7.2;
    Use a foreach loop instead

    View Slide

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

    View Slide

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

    View Slide

  22. $a = each($result);
    $b = each($result);

    View Slide

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

    View Slide

  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,
    ];
    }
    }

    View Slide

  25. Portable, unit-tested solution
    wrapped in a class.

    View Slide

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

    View Slide

  27. mcrypt
    PHP

    View Slide

  28. mcrypt
    PECL
    PHP

    View Slide

  29. mcrypt
    PECL
    PHP
    libmcrypt

    View Slide

  30. PHP
    openssl

    View Slide

  31. mcrypt.OFB != openssl.OFB

    View Slide

  32. How to test mcrypt to openssl upgrade?

    View Slide

  33. • Can we decrypt what we just encrypted?
    • Can we decrypt what we already have in our
    database?

    View Slide

  34. encrypted
    App with mcrypt
    original

    View Slide

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

    View Slide

  36. mysql_
    mysqli_
    PDO

    View Slide

  37. • Connection object needs to be available.
    • Connection object now a mysqli type.
    • Field metadata represented differently.
    • Different methods for unbuffered queries.

    View Slide

  38. mysql to mysqli conversion
    can't be unit-tested.

    View Slide

  39. mysql_connect
    mysql_query
    mysql_close
    mysqli_connect
    mysqli_query
    mysqli_close
    $connection

    View Slide

  40. /login
    redirect to profile
    credentials
    session
    database
    encryption

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. PHPCO won't detect everything.

    View Slide

  45. class MyClass extends BaseClass
    {
    public function validate($field, $value)
    {
    //...
    }
    }
    class BaseClass
    {
    public function validate(string $field, $value, $message = null)
    {
    //...
    }
    }

    View Slide

  46. View Slide

  47. • Start testing.
    • Note the errors that appear.
    • Tune static analysis for those specific errors.

    View Slide

  48. Write more tests.

    View Slide

  49. var_dump(0 == "");

    View Slide

  50. View Slide

  51. What about the framework?

    View Slide

  52. • New framework might be too different.
    • Is there a fork that keeps up with PHP?

    View Slide

  53. You could make the framework compatible.

    View Slide

  54. What about the tests?

    View Slide

  55. • Is PHPUnit compatible with PHP?
    • Can it be updated?
    • Might need to upgrade PHPUnit.

    View Slide

  56. Do not fix your tests and code
    at the same time.

    View Slide

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

    View Slide

  58. Tests
    PHP 8.0
    PHP 5.4 Application

    View Slide

  59. Tests
    PHP 8.0
    PHP 8.0 Application

    View Slide

  60. • Use PHPCompatibility
    • Use Psalm, PHPStan, etc.
    • Don't be afraid to fix the framework/libs.
    • Write tests.

    View Slide

  61. @afilina
    Questions?

    View Slide