SlideShare a Scribd company logo
1 of 47
Download to read offline
Dealing with Legacy PHP
Applications
Prepared by Clinton R. Nixon, Viget Labs
2007 July
What is a legacy application?
 Code you didn't write
 Code you wouldn't write
 Untested code
 Code with competing visions




Dealing with Legacy PHP
Applications
2007 Jul
What do we do with legacy code?
 We refactor!

 Refactoring is safely changing the
   implementation of code without changing
   the behavior of code.




Dealing with Legacy PHP
Applications
2007 Jul
Bad code smells
 What are some specific problems in legacy
  PHP code?
           ‣ No separation between PHP and HTML
           ‣ Lots of requires, few method calls
           ‣ Global variables




Dealing with Legacy PHP
Applications
2007 Jul
No separation between PHP and HTML
 <h1>Orders</h1>
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 foreach ($account->getOrders() as $order) {
      echo '<h2>' . $order['id'] . '</h2>';
      echo '<p>Status: ' . lookup_status($order['status_id']) .
      '<br />;
      echo 'Total: ';
      $total = array_reduce($order['purchases'],
      create_function('$a, $b', '$a += $b; return $a'));
      echo $total . '</p>';
 }
 ?>



Dealing with Legacy PHP
Applications
2007 Jul
Separating controllers and views
 Even without a solid MVC architecture, this helps
 You can do this in several safe and easy steps
 You absolutely will find pain points




Dealing with Legacy PHP
Applications
2007 Jul
Why do I need to do this?
 Your code complexity will decrease.
 echo isn't as fun as it looks.
 You will find hidden bugs and mistakes.




Dealing with Legacy PHP
Applications
2007 Jul
The simplest view class
 class View {
     protected static $VIEW_PATH = '/wherever/views/';
     public function assign($name, $value) {
           return $this->$name = $value;
     }


     public function render($filename) {
           $filename = self::$VIEW_PATH . $filename;
           if (is_file($filename)) {
              ob_start();
              include($filename);
               return ob_get_clean();
           }
     }
 }


Dealing with Legacy PHP
Applications
2007 Jul
Obvious improvements to make
 Error handling
 Assignment by reference
 Changing view path
 Display convenience method
 Use-specific subclasses with helper methods




Dealing with Legacy PHP
Applications
2007 Jul
The separation process
 Gather all your code
 Sift and separate controller from view code
 Assign variables to the view object
 Change all variable references in the view code
 Split the files
 Find duplicated views




Dealing with Legacy PHP
Applications
2007 Jul
The rules of view code
 Allowed:
           ‣ Control structures
           ‣ echo, or <?= $var ?>
           ‣ Display-specific functions, never nested
 Not allowed:
           ‣ Assignment
           ‣ Other function calls




Dealing with Legacy PHP
Applications
2007 Jul
Gather and sift code
 The step you won't like: gather all code for this
   controller
 Wipe brow
 Draw a line at the top of the code
 Move controller code above this line, fixing as
   necessary
           ‣ At this point, everything is view code




Dealing with Legacy PHP
Applications
2007 Jul
Code gathered
 <?php // View code goes below here ?>
 <h1>Orders</h1>
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 foreach ($account->getOrders() as $order) {
     echo '<h2>' . $order['id'] . '</h2>';
     echo '<p>Status: ' . lookup_status($order['status_id']) .
     '<br />';
     echo 'Total: ';
     $total = array_reduce($order['purchases'],
     create_function('$a, $b', '$a += $b; return $a'));
     echo $total . '</p>';
 }


Dealing with Legacy PHP
Applications
2007 Jul
Some controller code moved
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 ?>
 <?php // View code goes below here ?>
 <h1>Orders</h1>
 <?php foreach ($account->getOrders() as $order) { ?>
    <h2><?= $order['id'] ?></h2>
    <p>Status: <?= lookup_status($order['status_id']) ?>
    <br />
    Total: <?= array_reduce($order['purchases'], create_function(
      '$a, $b', '$a += $b; return $a')) ?>
    </p>
 <?php } ?>


Dealing with Legacy PHP
Applications
2007 Jul
Alternative control structures
 <?php if ($foo): ?>
 ...
 <?php endif; ?>

 <?php foreach ($this as $that): ?>
 ...
 <?php endforeach; ?>



Dealing with Legacy PHP
Applications
2007 Jul
Using alternative control structures
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 ?>
 <?php // View code goes below here ?>
 <h1>Orders</h1>
 <?php foreach ($account->getOrders() as $order): ?>
    <h2><?= $order['id'] ?></h2>
    <p>Status: <?= lookup_status($order['status_id']) ?>
    <br />
    Total: <?= array_reduce($order['purchases'], create_function(
      '$a, $b', '$a += $b; return $a')) ?>
    </p>
 <?php endforeach; ?>


Dealing with Legacy PHP
Applications
2007 Jul
A frustrating problem
 <?php foreach ($account->getOrders() as $order): ?>
     <h2><?= $order['id'] ?></h2>
     <p>Status: <?= lookup_status(
       $order['status_id']) ?>
     <br />
     Total: <?= array_reduce($order['purchases'],
       create_function('$a, $b',
         '$a += $b; return $a')) ?>
     </p>
 <?php endforeach; ?>




Dealing with Legacy PHP
Applications
2007 Jul
Dealing with this problem
 There are two approaches.
           ‣ You can create a new array of variables for
             your view.
           ‣ Or, you can encapsulate this logic in an object.




Dealing with Legacy PHP
Applications
2007 Jul
Our new order object
 <?php
 class Order {
      ...
           public function getStatus() {
           return lookup_status($this->getStatusId());
           }


           public function getTotal() {
           return array_reduce($this->getPurchases(),
              create_function('$a, $b', '$a += $b; return $a'));
      }
 }
 ?>


Dealing with Legacy PHP
Applications
2007 Jul
Logic removed from view code
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 $orders = $account->getOrders();
 ?>
 <?php // View code goes below here ?>
 <h1>Orders</h1>
 <?php foreach ($orders as $order): ?>
    <h2><?= $order->getId() ?></h2>
    <p>Status: <?= $order->getStatus() ?>
    <br />
    Total: <?= $order->getTotal() ?>
    </p>
 <?php endforeach; ?>


Dealing with Legacy PHP
Applications
2007 Jul
Change all variables to view object variables
 Assign variables to the view object:
           $view->assign('foo', $foo);
 One-by-one, change variables in view code.
 Test to convince yourself.
 You will probably iterate back to the
   previous step.
 Document inputs to the view.



Dealing with Legacy PHP
Applications
2007 Jul
View object created
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 $orders = $account->getOrders();
 $view = new View();
 $view->assign('orders', $orders);
 ?>
 <?php // View code goes below here ?>
 <h1>Orders</h1>
 <?php foreach ($view->orders as $order): ?>
    <h2><?= $order->getId() ?></h2>
    <p>Status: <?= $order->getStatus() ?>
    <br />
    Total: <?= $order->getTotal() ?>
    </p>
 <?php endforeach; ?>



Dealing with Legacy PHP
Applications
2007 Jul
Separate the files
 Create a new file for the view code.
 Important! Search and replace $view with $this.
 Test one more time.




Dealing with Legacy PHP
Applications
2007 Jul
Our two files
 <?php
 $account = new Account($account_id);
 $account->loadOrders();
 $orders = $account->getOrders();
 $view = new View();
 $view->assign('orders', $orders);
 $view->display('orders.tpl');
 ?>


 <h1>Orders</h1>
 <?php foreach ($view->orders as $order): ?>
      <h2><?= $order->getId() ?></h2>
      <p>Status: <?= $order->getStatus() ?>
      <br />
      Total: <?= $order->getTotal() ?>
      </p>
 <?php endforeach; ?>



Dealing with Legacy PHP
Applications
2007 Jul
Find duplicated views
 As you do this to multiple controllers, you will see
   repetition.
 There will probably be subtle differences.
 Take the time to re-work these so you can re-use
   view files.
 Note! You can include views in other views with:
           $this->render('included_file.tpl');




Dealing with Legacy PHP
Applications
2007 Jul
Using nested requires instead of function calls
 <?php
 require_once('db_setup_inc.php');
 require_once('account_auth_inc.php');
 require_once('i18n_inc.php');


 echo '<h1>Orders for account #' . $account_id .
     '</h1>';


 require('get_all_orders_inc.php');
 ...



Dealing with Legacy PHP
Applications
2007 Jul
Untangling a require web
 Require statements which call other require statements.
 Can be very complex.
 Dependent on application structure.




Dealing with Legacy PHP
Applications
2007 Jul
Reasons to untangle this web
 Remove unneeded complexity.
 Create less procedural code.
 Prior to PHP 5.2, require_once and include_once
    are more expensive than you would think.
 If you are requiring class definitions, and you have a
     standard file naming method, use __autoload().




Dealing with Legacy PHP
Applications
2007 Jul
The untangling process
 Identify inputs
 Identify outputs
 Wrap the file in a method
 Refactor method
 Move method to correct location




Dealing with Legacy PHP
Applications
2007 Jul
Identify inputs and outputs
 Find all variables expected to be set before this
    file is included.
 One possible way: execute this file by itself.
 Find all variables expected to be set or mutated
    by this file.
 Set variables are easy: comment out the require
    and watch the errors.
 Mutated is the set of inputs changed. Learn to
    search for these!


Dealing with Legacy PHP
Applications
2007 Jul
account_auth_inc.php
 <?php
 $auth_token = $_COOKIE['token'];


 if ($auth_token) {
     $acct_id = $db->GetOne('SELECT acct_id FROM
           logins WHERE auth_token = ?', array($auth_token));
 }
 if ($acct_id) {
     $acct = new Account($acct_id);
 } else {
     $acct = null;
 }
 $_COOKIE['token'] = gen_new_token($auth_token);


Dealing with Legacy PHP
Applications
2007 Jul
Wrap the file in a function
 Wrap the entire include in a function.
 Pass all input variables.
 Return all output variables as an array.
 And then, call that function at the bottom of the
   required file!
 This is a mess!




Dealing with Legacy PHP
Applications
2007 Jul
Function-wrapped
 <?php
 function account_auth($db, $auth_token) {
     if ($auth_token) {
         $acct_id = $db->GetOne('SELECT acct_id FROM
           logins WHERE auth_token = ?',
           array($auth_token));
     }
     if ($acct_id) {
         $acct = new Account($acct_id);
     } else {
         $acct = null;
     }
     return array($acct, gen_new_token($auth_token));
 }
 list($acct, $_COOKIE['token']) = account_auth($db, $_COOKIE['token']);


Dealing with Legacy PHP
Applications
2007 Jul
Refactor until complete
 Tease out the functions, or objects, inside this
     function.
 If you are returning a lot of data, see if it can be an
     object.
 Leave your temporary big function in place, so that
     your outside code doesn't break. Keep updating
     it to deal with your refactoring.




Dealing with Legacy PHP
Applications
2007 Jul
Moved token handling to Account
 <?php
 function account_auth($db, $auth_token) {
     // Instead of null, we now return an unloaded Account.
     $acct = new Account();
     if ($auth_token) {
           // SQL code from before
           $acct->loadFromToken($auth_token);
           // Token generation and cookie setting
           $acct->genNewToken($auth_token);
     }
     return $acct;
 }
 $acct = account_auth($db, $_COOKIE['token']);


Dealing with Legacy PHP
Applications
2007 Jul
Move to correct location
 Finally!
 Figure out where these functions or objects should
    live in your application.
 Move them there.
 Find where the require is called throughout your
    application, and replace that with your new
    function call or object method.




Dealing with Legacy PHP
Applications
2007 Jul
Global variables everywhere
 <?php
 $account_id = $_POST['acct_id'];
 $account = new Account($account_id);


 function getPurchases() {
     global $account;
     global $database;
     ...
 }


 function getLanguage() {
     global $account;
     global $database;
     global $i18n;
     ...
 }



Dealing with Legacy PHP
Applications
2007 Jul
Removing globals one by one
 Common globals:
           ‣ $_POST and $_GET
           ‣ Session or cookie data
           ‣ Database handles
           ‣ User account
           ‣ Language




Dealing with Legacy PHP
Applications
2007 Jul
Do you still have register_globals on?
 You may have heard: this is a bad idea.
 You may think that it will be impossible to fix.
 It's not. Turn on E_ALL.
 Spider your site and grep for uninitialized
    variables.
 It's some work, but not as hard as you think.
    It's worth it.



Dealing with Legacy PHP
Applications
2007 Jul
$_POST and $_GET
 These aren't horrible.
 But not horrible isn't a very high standard.
    class InputVariable {
           public   function   __construct($name) {...}
           public   function   isSet() {...}
           public   function   isGet() {...}
           public   function   isPost() {...}
           public   function   getAsString() {...}
           public   function   getAsInt() {...}
           ...
    }


Dealing with Legacy PHP
Applications
2007 Jul
The database global object
 Very common in PHP code
 Again, not horrible
 Prevents testing
 Prevents multiple databases




Dealing with Legacy PHP
Applications
2007 Jul
Parameterizing the DB handle
 Does it need to be everywhere?
 Can you pass it in to a function or to a
   constructor?
 The process is simple.
           ‣ Add database parameter.
           ‣ Pass in that global variable.
           ‣ If the call is not in global scope, find out how to
             pass in that variable to the current scope.
           ‣ Repeat.


Dealing with Legacy PHP
Applications
2007 Jul
Parameterizing globals
 <?php
 $account_id = $_POST['acct_id'];
 $account = new Account($database, $account_id);


 function getPurchases($account) {
     global $account;
     global $database;
     ...
 }


 function getLanguage($account, $i18n) {
     global $account;
     global $database;
     global $i18n;
     ...
 }



Dealing with Legacy PHP
Applications
2007 Jul
Maybe it does have to be everywhere
 Use a singleton.
 But not really.
 Make a way to change the singleton
   instance.
           ‣ Global define or environment variable.
           ‣ Static mutator.




Dealing with Legacy PHP
Applications
2007 Jul
A quick recap
 What are some specific problems in legacy
  PHP code?
           ‣ Mixed PHP and HTML – confusion between
             controller and view
           ‣ Use of require statements instead of function
             calls
           ‣ Unnecessary global variables causing
             dependencies




Dealing with Legacy PHP
Applications
2007 Jul
Further reading
 Working Effectively With Legacy Code,
   Michael Feathers
 Refactoring, Martin Fowler




Dealing with Legacy PHP
Applications
2007 Jul
Questions?
 clinton@viget.com

 Slides available at http://www.slideshare.net/viget
    and http://clintonrnixon.net.




Dealing with Legacy PHP
Applications
2007 Jul

More Related Content

What's hot

Clear php reference
Clear php referenceClear php reference
Clear php referenceDamien Seguy
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介Jace Ju
 
Using of TDD practices for Magento
Using of TDD practices for MagentoUsing of TDD practices for Magento
Using of TDD practices for MagentoIvan Chepurnyi
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedBaldur Rensch
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2Kacper Gunia
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
Optimizing Magento by Preloading Data
Optimizing Magento by Preloading DataOptimizing Magento by Preloading Data
Optimizing Magento by Preloading DataIvan Chepurnyi
 
Disregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDisregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDaniel Cousineau
 
Ruby on Rails For Java Programmers
Ruby on Rails For Java ProgrammersRuby on Rails For Java Programmers
Ruby on Rails For Java Programmerselliando dias
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016Kacper Gunia
 
Rapid Prototyping with PEAR
Rapid Prototyping with PEARRapid Prototyping with PEAR
Rapid Prototyping with PEARMarkus Wolff
 
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)Michael Wales
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Colin O'Dell
 
Error Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingError Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingSteve Maraspin
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 

What's hot (20)

Clear php reference
Clear php referenceClear php reference
Clear php reference
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 
Using of TDD practices for Magento
Using of TDD practices for MagentoUsing of TDD practices for Magento
Using of TDD practices for Magento
 
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learnedMoving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
Optimizing Magento by Preloading Data
Optimizing Magento by Preloading DataOptimizing Magento by Preloading Data
Optimizing Magento by Preloading Data
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
 
Disregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDisregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_Form
 
Ruby on Rails For Java Programmers
Ruby on Rails For Java ProgrammersRuby on Rails For Java Programmers
Ruby on Rails For Java Programmers
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
Rapid Prototyping with PEAR
Rapid Prototyping with PEARRapid Prototyping with PEAR
Rapid Prototyping with PEAR
 
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
 
Error Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingError Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, logging
 
PHPSpec BDD Framework
PHPSpec BDD FrameworkPHPSpec BDD Framework
PHPSpec BDD Framework
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 

Similar to Dealing With Legacy PHP Applications

Quality assurance for php projects with PHPStorm
Quality assurance for php projects with PHPStormQuality assurance for php projects with PHPStorm
Quality assurance for php projects with PHPStormMichelangelo van Dam
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterciconf
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkDirk Haun
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkBo-Yi Wu
 
[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress Development[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress DevelopmentAdam Tomat
 
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Atwix
 
Review unknown code with static analysis
Review unknown code with static analysisReview unknown code with static analysis
Review unknown code with static analysisDamien Seguy
 
How to Create Login and Registration API in PHP.pdf
How to Create Login and Registration API in PHP.pdfHow to Create Login and Registration API in PHP.pdf
How to Create Login and Registration API in PHP.pdfAppweb Coders
 
GettingStartedWithPHP
GettingStartedWithPHPGettingStartedWithPHP
GettingStartedWithPHPNat Weerawan
 
Iterators & generators: practical uses in memory management
Iterators & generators: practical uses in memory managementIterators & generators: practical uses in memory management
Iterators & generators: practical uses in memory managementAdrian Cardenas
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress DeveloperJoey Kudish
 
WordCamp KC 2012: Simplifying Theme Functionality
WordCamp KC 2012: Simplifying Theme FunctionalityWordCamp KC 2012: Simplifying Theme Functionality
WordCamp KC 2012: Simplifying Theme FunctionalityChip Bennett
 
Intro to php
Intro to phpIntro to php
Intro to phpSp Singh
 

Similar to Dealing With Legacy PHP Applications (20)

Quality assurance for php projects with PHPStorm
Quality assurance for php projects with PHPStormQuality assurance for php projects with PHPStorm
Quality assurance for php projects with PHPStorm
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniter
 
What's new with PHP7
What's new with PHP7What's new with PHP7
What's new with PHP7
 
Introduction to PHP
Introduction to PHPIntroduction to PHP
Introduction to PHP
 
4. Php MongoDB view_data
4. Php MongoDB view_data4. Php MongoDB view_data
4. Php MongoDB view_data
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application Framework
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC Framework
 
[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress Development[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress Development
 
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
 
Review unknown code with static analysis
Review unknown code with static analysisReview unknown code with static analysis
Review unknown code with static analysis
 
Zend Framework
Zend FrameworkZend Framework
Zend Framework
 
How to Create Login and Registration API in PHP.pdf
How to Create Login and Registration API in PHP.pdfHow to Create Login and Registration API in PHP.pdf
How to Create Login and Registration API in PHP.pdf
 
GettingStartedWithPHP
GettingStartedWithPHPGettingStartedWithPHP
GettingStartedWithPHP
 
Iterators & generators: practical uses in memory management
Iterators & generators: practical uses in memory managementIterators & generators: practical uses in memory management
Iterators & generators: practical uses in memory management
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress Developer
 
WordCamp KC 2012: Simplifying Theme Functionality
WordCamp KC 2012: Simplifying Theme FunctionalityWordCamp KC 2012: Simplifying Theme Functionality
WordCamp KC 2012: Simplifying Theme Functionality
 
php ali.docx
php ali.docxphp ali.docx
php ali.docx
 
Yii Introduction
Yii IntroductionYii Introduction
Yii Introduction
 
Intro to php
Intro to phpIntro to php
Intro to php
 

More from Viget Labs

Building a Brand as Consumers Take Control
Building a Brand as Consumers Take ControlBuilding a Brand as Consumers Take Control
Building a Brand as Consumers Take ControlViget Labs
 
Branded Utility By Josh Chambers
Branded Utility By Josh ChambersBranded Utility By Josh Chambers
Branded Utility By Josh ChambersViget Labs
 
Make Everyone a Tester: Natural Language Acceptance Testing
Make Everyone a Tester: Natural Language Acceptance TestingMake Everyone a Tester: Natural Language Acceptance Testing
Make Everyone a Tester: Natural Language Acceptance TestingViget Labs
 
Women In Technology
Women In TechnologyWomen In Technology
Women In TechnologyViget Labs
 
9 Tips to Profitability: How Squidoo Did It
9 Tips to Profitability: How Squidoo Did It9 Tips to Profitability: How Squidoo Did It
9 Tips to Profitability: How Squidoo Did ItViget Labs
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful RailsViget Labs
 
Cleanliness is Next to Domain-Specificity
Cleanliness is Next to Domain-SpecificityCleanliness is Next to Domain-Specificity
Cleanliness is Next to Domain-SpecificityViget Labs
 
Changing Your Mindset: Getting Started With Test-Driven Development
Changing Your Mindset: Getting Started With Test-Driven DevelopmentChanging Your Mindset: Getting Started With Test-Driven Development
Changing Your Mindset: Getting Started With Test-Driven DevelopmentViget Labs
 
Mockfight! FlexMock vs. Mocha
Mockfight! FlexMock vs. MochaMockfight! FlexMock vs. Mocha
Mockfight! FlexMock vs. MochaViget Labs
 
Building and Working With Static Sites in Ruby on Rails
Building and Working With Static Sites in Ruby on RailsBuilding and Working With Static Sites in Ruby on Rails
Building and Working With Static Sites in Ruby on RailsViget Labs
 

More from Viget Labs (11)

Building a Brand as Consumers Take Control
Building a Brand as Consumers Take ControlBuilding a Brand as Consumers Take Control
Building a Brand as Consumers Take Control
 
Branded Utility By Josh Chambers
Branded Utility By Josh ChambersBranded Utility By Josh Chambers
Branded Utility By Josh Chambers
 
Make Everyone a Tester: Natural Language Acceptance Testing
Make Everyone a Tester: Natural Language Acceptance TestingMake Everyone a Tester: Natural Language Acceptance Testing
Make Everyone a Tester: Natural Language Acceptance Testing
 
Women In Technology
Women In TechnologyWomen In Technology
Women In Technology
 
9 Tips to Profitability: How Squidoo Did It
9 Tips to Profitability: How Squidoo Did It9 Tips to Profitability: How Squidoo Did It
9 Tips to Profitability: How Squidoo Did It
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful Rails
 
Hows Haml?
Hows Haml?Hows Haml?
Hows Haml?
 
Cleanliness is Next to Domain-Specificity
Cleanliness is Next to Domain-SpecificityCleanliness is Next to Domain-Specificity
Cleanliness is Next to Domain-Specificity
 
Changing Your Mindset: Getting Started With Test-Driven Development
Changing Your Mindset: Getting Started With Test-Driven DevelopmentChanging Your Mindset: Getting Started With Test-Driven Development
Changing Your Mindset: Getting Started With Test-Driven Development
 
Mockfight! FlexMock vs. Mocha
Mockfight! FlexMock vs. MochaMockfight! FlexMock vs. Mocha
Mockfight! FlexMock vs. Mocha
 
Building and Working With Static Sites in Ruby on Rails
Building and Working With Static Sites in Ruby on RailsBuilding and Working With Static Sites in Ruby on Rails
Building and Working With Static Sites in Ruby on Rails
 

Recently uploaded

FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756dollysharma2066
 
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876dlhescort
 
Cracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxCracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxWorkforce Group
 
Call Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine ServiceCall Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine Serviceritikaroy0888
 
Organizational Transformation Lead with Culture
Organizational Transformation Lead with CultureOrganizational Transformation Lead with Culture
Organizational Transformation Lead with CultureSeta Wicaksana
 
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...amitlee9823
 
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangalore
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service BangaloreCall Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangalore
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangaloreamitlee9823
 
It will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayIt will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayNZSG
 
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...Sheetaleventcompany
 
John Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfJohn Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfAmzadHosen3
 
Falcon Invoice Discounting: The best investment platform in india for investors
Falcon Invoice Discounting: The best investment platform in india for investorsFalcon Invoice Discounting: The best investment platform in india for investors
Falcon Invoice Discounting: The best investment platform in india for investorsFalcon Invoice Discounting
 
How to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityHow to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityEric T. Tung
 
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLMONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLSeo
 
Falcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to ProsperityFalcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to Prosperityhemanthkumar470700
 
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...lizamodels9
 
Monthly Social Media Update April 2024 pptx.pptx
Monthly Social Media Update April 2024 pptx.pptxMonthly Social Media Update April 2024 pptx.pptx
Monthly Social Media Update April 2024 pptx.pptxAndy Lambert
 
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...amitlee9823
 
A DAY IN THE LIFE OF A SALESMAN / WOMAN
A DAY IN THE LIFE OF A  SALESMAN / WOMANA DAY IN THE LIFE OF A  SALESMAN / WOMAN
A DAY IN THE LIFE OF A SALESMAN / WOMANIlamathiKannappan
 
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...amitlee9823
 

Recently uploaded (20)

FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Majnu Ka Tilla, Delhi Contact Us 8377877756
 
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876
Call Girls in Delhi, Escort Service Available 24x7 in Delhi 959961-/-3876
 
Cracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxCracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptx
 
Call Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine ServiceCall Girls In Panjim North Goa 9971646499 Genuine Service
Call Girls In Panjim North Goa 9971646499 Genuine Service
 
Organizational Transformation Lead with Culture
Organizational Transformation Lead with CultureOrganizational Transformation Lead with Culture
Organizational Transformation Lead with Culture
 
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...
Call Girls Kengeri Satellite Town Just Call 👗 7737669865 👗 Top Class Call Gir...
 
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangalore
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service BangaloreCall Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangalore
Call Girls Hebbal Just Call 👗 7737669865 👗 Top Class Call Girl Service Bangalore
 
It will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 MayIt will be International Nurses' Day on 12 May
It will be International Nurses' Day on 12 May
 
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...
Chandigarh Escorts Service 📞8868886958📞 Just📲 Call Nihal Chandigarh Call Girl...
 
John Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfJohn Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdf
 
Falcon Invoice Discounting: The best investment platform in india for investors
Falcon Invoice Discounting: The best investment platform in india for investorsFalcon Invoice Discounting: The best investment platform in india for investors
Falcon Invoice Discounting: The best investment platform in india for investors
 
How to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityHow to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League City
 
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLMONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
 
Falcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to ProsperityFalcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to Prosperity
 
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
 
Monthly Social Media Update April 2024 pptx.pptx
Monthly Social Media Update April 2024 pptx.pptxMonthly Social Media Update April 2024 pptx.pptx
Monthly Social Media Update April 2024 pptx.pptx
 
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
 
Falcon Invoice Discounting platform in india
Falcon Invoice Discounting platform in indiaFalcon Invoice Discounting platform in india
Falcon Invoice Discounting platform in india
 
A DAY IN THE LIFE OF A SALESMAN / WOMAN
A DAY IN THE LIFE OF A  SALESMAN / WOMANA DAY IN THE LIFE OF A  SALESMAN / WOMAN
A DAY IN THE LIFE OF A SALESMAN / WOMAN
 
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...
Nelamangala Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore...
 

Dealing With Legacy PHP Applications

  • 1. Dealing with Legacy PHP Applications Prepared by Clinton R. Nixon, Viget Labs 2007 July
  • 2. What is a legacy application? Code you didn't write Code you wouldn't write Untested code Code with competing visions Dealing with Legacy PHP Applications 2007 Jul
  • 3. What do we do with legacy code? We refactor! Refactoring is safely changing the implementation of code without changing the behavior of code. Dealing with Legacy PHP Applications 2007 Jul
  • 4. Bad code smells What are some specific problems in legacy PHP code? ‣ No separation between PHP and HTML ‣ Lots of requires, few method calls ‣ Global variables Dealing with Legacy PHP Applications 2007 Jul
  • 5. No separation between PHP and HTML <h1>Orders</h1> <?php $account = new Account($account_id); $account->loadOrders(); foreach ($account->getOrders() as $order) { echo '<h2>' . $order['id'] . '</h2>'; echo '<p>Status: ' . lookup_status($order['status_id']) . '<br />; echo 'Total: '; $total = array_reduce($order['purchases'], create_function('$a, $b', '$a += $b; return $a')); echo $total . '</p>'; } ?> Dealing with Legacy PHP Applications 2007 Jul
  • 6. Separating controllers and views Even without a solid MVC architecture, this helps You can do this in several safe and easy steps You absolutely will find pain points Dealing with Legacy PHP Applications 2007 Jul
  • 7. Why do I need to do this? Your code complexity will decrease. echo isn't as fun as it looks. You will find hidden bugs and mistakes. Dealing with Legacy PHP Applications 2007 Jul
  • 8. The simplest view class class View { protected static $VIEW_PATH = '/wherever/views/'; public function assign($name, $value) { return $this->$name = $value; } public function render($filename) { $filename = self::$VIEW_PATH . $filename; if (is_file($filename)) { ob_start(); include($filename); return ob_get_clean(); } } } Dealing with Legacy PHP Applications 2007 Jul
  • 9. Obvious improvements to make Error handling Assignment by reference Changing view path Display convenience method Use-specific subclasses with helper methods Dealing with Legacy PHP Applications 2007 Jul
  • 10. The separation process Gather all your code Sift and separate controller from view code Assign variables to the view object Change all variable references in the view code Split the files Find duplicated views Dealing with Legacy PHP Applications 2007 Jul
  • 11. The rules of view code Allowed: ‣ Control structures ‣ echo, or <?= $var ?> ‣ Display-specific functions, never nested Not allowed: ‣ Assignment ‣ Other function calls Dealing with Legacy PHP Applications 2007 Jul
  • 12. Gather and sift code The step you won't like: gather all code for this controller Wipe brow Draw a line at the top of the code Move controller code above this line, fixing as necessary ‣ At this point, everything is view code Dealing with Legacy PHP Applications 2007 Jul
  • 13. Code gathered <?php // View code goes below here ?> <h1>Orders</h1> <?php $account = new Account($account_id); $account->loadOrders(); foreach ($account->getOrders() as $order) { echo '<h2>' . $order['id'] . '</h2>'; echo '<p>Status: ' . lookup_status($order['status_id']) . '<br />'; echo 'Total: '; $total = array_reduce($order['purchases'], create_function('$a, $b', '$a += $b; return $a')); echo $total . '</p>'; } Dealing with Legacy PHP Applications 2007 Jul
  • 14. Some controller code moved <?php $account = new Account($account_id); $account->loadOrders(); ?> <?php // View code goes below here ?> <h1>Orders</h1> <?php foreach ($account->getOrders() as $order) { ?> <h2><?= $order['id'] ?></h2> <p>Status: <?= lookup_status($order['status_id']) ?> <br /> Total: <?= array_reduce($order['purchases'], create_function( '$a, $b', '$a += $b; return $a')) ?> </p> <?php } ?> Dealing with Legacy PHP Applications 2007 Jul
  • 15. Alternative control structures <?php if ($foo): ?> ... <?php endif; ?> <?php foreach ($this as $that): ?> ... <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 16. Using alternative control structures <?php $account = new Account($account_id); $account->loadOrders(); ?> <?php // View code goes below here ?> <h1>Orders</h1> <?php foreach ($account->getOrders() as $order): ?> <h2><?= $order['id'] ?></h2> <p>Status: <?= lookup_status($order['status_id']) ?> <br /> Total: <?= array_reduce($order['purchases'], create_function( '$a, $b', '$a += $b; return $a')) ?> </p> <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 17. A frustrating problem <?php foreach ($account->getOrders() as $order): ?> <h2><?= $order['id'] ?></h2> <p>Status: <?= lookup_status( $order['status_id']) ?> <br /> Total: <?= array_reduce($order['purchases'], create_function('$a, $b', '$a += $b; return $a')) ?> </p> <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 18. Dealing with this problem There are two approaches. ‣ You can create a new array of variables for your view. ‣ Or, you can encapsulate this logic in an object. Dealing with Legacy PHP Applications 2007 Jul
  • 19. Our new order object <?php class Order { ... public function getStatus() { return lookup_status($this->getStatusId()); } public function getTotal() { return array_reduce($this->getPurchases(), create_function('$a, $b', '$a += $b; return $a')); } } ?> Dealing with Legacy PHP Applications 2007 Jul
  • 20. Logic removed from view code <?php $account = new Account($account_id); $account->loadOrders(); $orders = $account->getOrders(); ?> <?php // View code goes below here ?> <h1>Orders</h1> <?php foreach ($orders as $order): ?> <h2><?= $order->getId() ?></h2> <p>Status: <?= $order->getStatus() ?> <br /> Total: <?= $order->getTotal() ?> </p> <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 21. Change all variables to view object variables Assign variables to the view object: $view->assign('foo', $foo); One-by-one, change variables in view code. Test to convince yourself. You will probably iterate back to the previous step. Document inputs to the view. Dealing with Legacy PHP Applications 2007 Jul
  • 22. View object created <?php $account = new Account($account_id); $account->loadOrders(); $orders = $account->getOrders(); $view = new View(); $view->assign('orders', $orders); ?> <?php // View code goes below here ?> <h1>Orders</h1> <?php foreach ($view->orders as $order): ?> <h2><?= $order->getId() ?></h2> <p>Status: <?= $order->getStatus() ?> <br /> Total: <?= $order->getTotal() ?> </p> <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 23. Separate the files Create a new file for the view code. Important! Search and replace $view with $this. Test one more time. Dealing with Legacy PHP Applications 2007 Jul
  • 24. Our two files <?php $account = new Account($account_id); $account->loadOrders(); $orders = $account->getOrders(); $view = new View(); $view->assign('orders', $orders); $view->display('orders.tpl'); ?> <h1>Orders</h1> <?php foreach ($view->orders as $order): ?> <h2><?= $order->getId() ?></h2> <p>Status: <?= $order->getStatus() ?> <br /> Total: <?= $order->getTotal() ?> </p> <?php endforeach; ?> Dealing with Legacy PHP Applications 2007 Jul
  • 25. Find duplicated views As you do this to multiple controllers, you will see repetition. There will probably be subtle differences. Take the time to re-work these so you can re-use view files. Note! You can include views in other views with: $this->render('included_file.tpl'); Dealing with Legacy PHP Applications 2007 Jul
  • 26. Using nested requires instead of function calls <?php require_once('db_setup_inc.php'); require_once('account_auth_inc.php'); require_once('i18n_inc.php'); echo '<h1>Orders for account #' . $account_id . '</h1>'; require('get_all_orders_inc.php'); ... Dealing with Legacy PHP Applications 2007 Jul
  • 27. Untangling a require web Require statements which call other require statements. Can be very complex. Dependent on application structure. Dealing with Legacy PHP Applications 2007 Jul
  • 28. Reasons to untangle this web Remove unneeded complexity. Create less procedural code. Prior to PHP 5.2, require_once and include_once are more expensive than you would think. If you are requiring class definitions, and you have a standard file naming method, use __autoload(). Dealing with Legacy PHP Applications 2007 Jul
  • 29. The untangling process Identify inputs Identify outputs Wrap the file in a method Refactor method Move method to correct location Dealing with Legacy PHP Applications 2007 Jul
  • 30. Identify inputs and outputs Find all variables expected to be set before this file is included. One possible way: execute this file by itself. Find all variables expected to be set or mutated by this file. Set variables are easy: comment out the require and watch the errors. Mutated is the set of inputs changed. Learn to search for these! Dealing with Legacy PHP Applications 2007 Jul
  • 31. account_auth_inc.php <?php $auth_token = $_COOKIE['token']; if ($auth_token) { $acct_id = $db->GetOne('SELECT acct_id FROM logins WHERE auth_token = ?', array($auth_token)); } if ($acct_id) { $acct = new Account($acct_id); } else { $acct = null; } $_COOKIE['token'] = gen_new_token($auth_token); Dealing with Legacy PHP Applications 2007 Jul
  • 32. Wrap the file in a function Wrap the entire include in a function. Pass all input variables. Return all output variables as an array. And then, call that function at the bottom of the required file! This is a mess! Dealing with Legacy PHP Applications 2007 Jul
  • 33. Function-wrapped <?php function account_auth($db, $auth_token) { if ($auth_token) { $acct_id = $db->GetOne('SELECT acct_id FROM logins WHERE auth_token = ?', array($auth_token)); } if ($acct_id) { $acct = new Account($acct_id); } else { $acct = null; } return array($acct, gen_new_token($auth_token)); } list($acct, $_COOKIE['token']) = account_auth($db, $_COOKIE['token']); Dealing with Legacy PHP Applications 2007 Jul
  • 34. Refactor until complete Tease out the functions, or objects, inside this function. If you are returning a lot of data, see if it can be an object. Leave your temporary big function in place, so that your outside code doesn't break. Keep updating it to deal with your refactoring. Dealing with Legacy PHP Applications 2007 Jul
  • 35. Moved token handling to Account <?php function account_auth($db, $auth_token) { // Instead of null, we now return an unloaded Account. $acct = new Account(); if ($auth_token) { // SQL code from before $acct->loadFromToken($auth_token); // Token generation and cookie setting $acct->genNewToken($auth_token); } return $acct; } $acct = account_auth($db, $_COOKIE['token']); Dealing with Legacy PHP Applications 2007 Jul
  • 36. Move to correct location Finally! Figure out where these functions or objects should live in your application. Move them there. Find where the require is called throughout your application, and replace that with your new function call or object method. Dealing with Legacy PHP Applications 2007 Jul
  • 37. Global variables everywhere <?php $account_id = $_POST['acct_id']; $account = new Account($account_id); function getPurchases() { global $account; global $database; ... } function getLanguage() { global $account; global $database; global $i18n; ... } Dealing with Legacy PHP Applications 2007 Jul
  • 38. Removing globals one by one Common globals: ‣ $_POST and $_GET ‣ Session or cookie data ‣ Database handles ‣ User account ‣ Language Dealing with Legacy PHP Applications 2007 Jul
  • 39. Do you still have register_globals on? You may have heard: this is a bad idea. You may think that it will be impossible to fix. It's not. Turn on E_ALL. Spider your site and grep for uninitialized variables. It's some work, but not as hard as you think. It's worth it. Dealing with Legacy PHP Applications 2007 Jul
  • 40. $_POST and $_GET These aren't horrible. But not horrible isn't a very high standard. class InputVariable { public function __construct($name) {...} public function isSet() {...} public function isGet() {...} public function isPost() {...} public function getAsString() {...} public function getAsInt() {...} ... } Dealing with Legacy PHP Applications 2007 Jul
  • 41. The database global object Very common in PHP code Again, not horrible Prevents testing Prevents multiple databases Dealing with Legacy PHP Applications 2007 Jul
  • 42. Parameterizing the DB handle Does it need to be everywhere? Can you pass it in to a function or to a constructor? The process is simple. ‣ Add database parameter. ‣ Pass in that global variable. ‣ If the call is not in global scope, find out how to pass in that variable to the current scope. ‣ Repeat. Dealing with Legacy PHP Applications 2007 Jul
  • 43. Parameterizing globals <?php $account_id = $_POST['acct_id']; $account = new Account($database, $account_id); function getPurchases($account) { global $account; global $database; ... } function getLanguage($account, $i18n) { global $account; global $database; global $i18n; ... } Dealing with Legacy PHP Applications 2007 Jul
  • 44. Maybe it does have to be everywhere Use a singleton. But not really. Make a way to change the singleton instance. ‣ Global define or environment variable. ‣ Static mutator. Dealing with Legacy PHP Applications 2007 Jul
  • 45. A quick recap What are some specific problems in legacy PHP code? ‣ Mixed PHP and HTML – confusion between controller and view ‣ Use of require statements instead of function calls ‣ Unnecessary global variables causing dependencies Dealing with Legacy PHP Applications 2007 Jul
  • 46. Further reading Working Effectively With Legacy Code, Michael Feathers Refactoring, Martin Fowler Dealing with Legacy PHP Applications 2007 Jul
  • 47. Questions? clinton@viget.com Slides available at http://www.slideshare.net/viget and http://clintonrnixon.net. Dealing with Legacy PHP Applications 2007 Jul