hunt for dead code
TRANSCRIPT
Dead code
❖ Code that is never used
❖ Data never delivered to the user
❖ Opposite to :
PHP Fatal error: Uncaught Error: Call to undefined function foo()
Dead code<?php
class A implements B {}
interface B {}
?>
<?php interface B implementedBy A,B,C {}
class A {}
?>
Wouldn't it be better to hunt dead code to write this?
Dead code
❖ PHP base install
❖ 766 functions
❖ 92 classes
❖ 1024 constants
❖ Avoid compiling too many PHP extensions
❖ Use disable_functions
Why remove?
❖ Larger code source
❖ Less maintainable code
❖ Dead code is often maintained
❖ Slower code
❖ Dead code grows over time
Why keep dead code?
❖ No one was ever fired to keep dead code
❖ Why fix something that isn't broken
❖ All tests for this feature are OK
❖ Other part of the code depends on it
❖ Code base always grows organically
❖ No time for that!
How to remove dead code
❖ Spot the original code
❖ Check for its usage
❖ Remove usages one by one
❖ Remove the original code
Unreachable code
<?php
if (false) { print_r($variable); }
if (0) { // Huge piece of code with bug // or unfinished new feature } ?>
❖ Classic debug
Unreachable code
<?php
function foo($bar) { for($i = 0; $i < 10; $i++) { $bar *= 2; exit; // or die $bar += 1; } }
?>
❖ Classic unreachable
Unreachable code
<?php
function foo($bar) { for($i = 0; $i < 10; $i++) { $bar *= 2; continue; $bar += 1; } }
?>
❖ Classic unreachable
Unreachable code
<?php
function foo($bar) { for($i = 0; $i < 10; $i++) { $bar *= 2; break 1; $bar += 1; } }
?>
❖ Classic unreachable
Unreachable code
<?php
function foo($bar) { $bar *= 2; return $bar;
$bar += 1; return $bar; }
?>
❖ Classic unreachable
Unreachable code
<?php
$bar *= 2; goto END: $bar += 1; exit; end: print $bar;
?>
❖ Classic unreachable
Unreachable code
<?php
$bar = 2; goto END; class X { const ONE = 1; }
exit; END: print $bar . X::ONE; ?>
❖ Classic unreachable
❖ But definitions are still accessible
Useless variables<?php
$usedOnce = "Hello world";
?>
❖ The mythical 'used once' variable
❖ 75% repo has this kind of variable
❖ Global or local to functions
❖ Mass import $_GET/_POST
<?php
echo $alsoUsedOnce;
?>
Useless variables
<?php
function foo() { $a = "Hello world"; //more code $a = "Hello universe"; //more code $a .= " and the world"; }
?>
❖ Written only variables
❖ Inside a scope
❖ Read-only is better but worth a check
Default clause in switch()<?php
switch($x) { case '1' : break; default : break; default : break; case '2' : break; }
❖ PHP 7.0+ : Fatal error
❖ 'case' order is "not" important
Multiple cases ?
❖ Switch() uses ==
❖ Transtyping happens
switch($x) { case 1 : break; case 0+1 : break; case '1' : break; case true : break; case 1.0 : break; case $y : break; }
Arrays index
<?php
$a = [ true => 1, 1.0 => 2, 1.1 => 3, 4, "1.4" => 5, 2 => 6]; print_r($a);
?>
❖ are int or strings
❖ Beware of mix between auto-indexing andfixed values Array
( [1] => 3 [2] => 6 [1.4] => 5 )
Dead code in plain sight<?php
try { doSomething(); } catch (NotAnException $e) {
} catch (MyExxeption $e) {
} catch (\Exception $e) {
} catch (MyException $e) {
}
❖ Non Existing classes
❖ Non-Exceptions
❖ Specific to general
❖ Simply ignored
Instanceof
❖ The target class is in the current namespace
<?php
class MyClass {}
$o = new MyClass();
if ($o instanceof MyClass) { print "Found MyClass\n"; }
?>
Instanceof
❖ The target class is in the current namespace
❖ Beware of moving classes and instanceof around
<?php
namespace { class MyClass {} }
namespace X { $o = new \MyClass();
if ($o instanceof MyClass) { print "Found MyClass\n"; } }
?>
Instanceof
❖ Hardcoded names vsstrings classnames
namespace { class MyClass {} }
namespace X { $o = new \MyClass();
$a = '\\X\\MyClass'; if ($o instanceof $a) { print "Found MyClass with \$a\n"; } }
Typehint
❖ Typehint are not checked either
❖ You can check type hint at execution time
<?php
class foo {}
$o = new foo();
function bar(fooo $a) {}
bar($o); ?>
PHP Fatal error: Uncaught TypeError: Argument 1 passed to bar() must be an instance of fooo, instance of foo given,
Traits
❖ Used in class-'use' expression
❖ Depends on 'use' expression
❖ Used in static method call, static properties
<?php
trait t1 { use t2; }
class c { use t1; } ?>
Traits
❖ Easy to spot : only static calls
❖ Namespaced, aliased
❖ Trait local dependencies leads to more dead code
<?php
use t1 as t3;
trait t1 { use t2; }
class c { use t3; } ?>
Traits<?php
trait t { function setName($name) { $this->name = $this->normalize($name); } }
class bar { use t; private $name = 'none';
function normalize($string) {} } ?>
Interfaces
❖ Used in classes
❖ Used in interfaces
❖ Used in static constants
❖ Used in instanceof, catch and type hint
<?php
interface i2 { const konst = 3; }
interface i1 implements i2 { }
class c implements i1 { }
echo i2::konst;
?>
Interfaces
❖ It may end up in variables/literals
❖ It inherits from parents
<?php
interface i2 { const konst = 3; }
interface i1 implements i2 { }
$interfaceName = 'i2';
if ($object instanceof $interfaceName) {}
echo i1::konst;
?>
Classes
❖ Used in other classes
❖ Used in all static calls
❖ constants, properties, methods
❖ Normal calls ->
❖ Used in instanceof, catch, typehint
❖ Used in new
<?php
class c1 { }
class c2 extends c1 { }
new c2();
?>
Classes
❖ new a / new a()
❖ Dynamic calls all over the place
❖ It has special keywords : parent, self, static
<?php
class foo { const ONE = 1; const TWO = self::ONE + 1; }
class bar extends foo { const THREE = parent::TWO + self::ONE; }
$class = 'bar'; $o = new \bar;
?>
Classes
❖ Circular dependencies
❖ Actually applies to traits and interfaces as well
<?php
class foo extends bar { const TWO = bar::ONE + 1; }
class bar extends foo { const ONE = bar::ONE; }
?>PHP Fatal error: Class 'bar' not found
Classes
❖ Vicious circular dependencies
<?php
class foo { const ONE = 1; const TWO = bar::ONE + 1; }
class bar { const ONE = foo::TWO; }
echo bar::ONE;
PHP Fatal error: Uncaught Error: Cannot declare self-referencing constant 'foo::TWO' on line 12
Functions
❖ Used in function calls
❖ Depends on namespaces and aliases
❖ Used in native functions like array_map()
<?php
function foo() {}
array_map('foo', $array);
?>
Constants
❖ Defined with const, define
❖ May be case-insensitive
❖ Dynamic constant()
❖ Namespaced, aliased
❖ Used in static expressions
❖ This leads to inclusion hells
<?php
define(ONE, 1, true);
const TWO = ONE + 1;
$constant = 'TWO'; echo constant($constant);
?>
Inclusion
❖ include/require, /_once
❖ new() because autoload
❖ Static constant, static methods calls, static property
❖ Order is important, as include also execute…
<?php
include 'file.php';
?>
How to spot issues?
❖ Code knowledge
❖ lint
❖ Grep / Search
❖ Static analysis
❖ Logs / error_reporting
❖ Unit Tests
When to hunt dead code?❖ An every day relaxation
❖ My most productive day saw 200 classes removed
❖ trigger_error($msg, E_USER_DEPRECATED) and debug_backtrace()
❖ Bath in the energy of live code
Lorem Ipsum Dolor
Thanks!
@exakathttp://www.exakat.io/http://slideshare.net/dseguy/
Dynamic variables
<?php
$a = 'b'; $b = 'c'; $c = 'd';
$$${$a} = '3'; echo $d;
?>
❖ Variable variables
❖ Extract()
❖ parse_str(), one arg
Unreachable code
<?php
if ($sale_price === $regular_price || $sale_price !== $price) { print_r($variable); }
?>
Unreachable code
$sale_price $regular $price results
$regular 1 1 1
$price 0 0 0
All other values
0 1 1
($sale_price === $regular_price|| $sale_price !== $price)