How to Find Unused Functions in Your PHP Project

As a developer, maintaining a clean and efficient codebase is crucial for the long-term health of any project. One common issue that can bloat your code and contribute to technical debt is having unused functions. In this blog post, we’ll explore how you can effectively identify these unused functions in your PHP projects, using both built-in PHP features and a custom script.

The Importance of Identifying Unused Functions

Unused functions can lead to:

  • Wasted Resources: Extra code takes up space and can slow down your application.
  • Complexity: More code means more complexity, making your project harder to navigate and maintain.
  • Security Risks: Unused code might inadvertently expose vulnerabilities that are yet to be discovered.

Finding and removing these functions is essential for optimal code management.

Using PHP Features to Locate Unused Functions

PHP offers some built-in features that you can leverage to analyze your codebase. Two notable features include:

  • Reflection: It provides the ability to reflect on classes, methods, and functions to understand their usage.
  • Tokenization with token_get_all(): This function allows you to parse PHP code into tokens, making it easier to identify the structure of the code.

However, relying solely on these features might not give you a complete picture, which is where custom scripting can help.

A Quick and Dirty Solution: Custom PHP Script

Based on recent research and community feedback, here’s a custom script to help you find unused functions in your PHP project. This script scans your project directory, identifies function definitions and their references, and presents the results in a readable format.

Step 1: Setting Up Your Environment

  • Make sure you have a PHP environment set up.
  • Ensure that your project directory path is accurate.
  • Copy the following script into a new .php file.

Step 2: The PHP Script

<?php
$functions = array();
$path = "/path/to/my/php/project"; // Update this path to your project directory.
define_dir($path, $functions);
reference_dir($path, $functions);
echo "<table>";
echo "<tr><th>Name</th><th>Defined</th><th>Referenced</th></tr>";
foreach ($functions as $name => $value) {
    echo "<tr><td>" . htmlentities($name) . "</td>";
    echo "<td>" . (isset($value[0]) ? count($value[0]) : "-") . "</td>";
    echo "<td>" . (isset($value[1]) ? count($value[1]) : "-") . "</td></tr>";
}
echo "</table>";

function define_dir($path, &$functions) {
    if ($dir = opendir($path)) {
        while (($file = readdir($dir)) !== false) {
            if (substr($file, 0, 1) == ".") continue;
            if (is_dir($path . "/" . $file)) {
                define_dir($path . "/" . $file, $functions);
            } else {
                if (substr($file, - 4) != ".php") continue;
                define_file($path . "/" . $file, $functions);
            }
        }
    }       
}

function define_file($path, &$functions) {
    $tokens = token_get_all(file_get_contents($path));
    for ($i = 0; $i < count($tokens); $i++) {
        $token = $tokens[$i];
        if (is_array($token) && $token[0] == T_FUNCTION) {
            $i++;
            $token = $tokens[$i + 1]; // Get the function name next
            if (is_array($token) && $token[0] == T_STRING) {
                $functions[$token[1]][0][] = array($path, $token[2]); // Store the function definition
            }
        }
    }
}

function reference_dir($path, &$functions) {
    if ($dir = opendir($path)) {
        while (($file = readdir($dir)) !== false) {
            if (substr($file, 0, 1) == ".") continue;
            if (is_dir($path . "/" . $file)) {
                reference_dir($path . "/" . $file, $functions);
            } else {
                if (substr($file, - 4) != ".php") continue;
                reference_file($path . "/" . $file, $functions);
            }
        }
    }       
}

function reference_file($path, &$functions) {
    $tokens = token_get_all(file_get_contents($path));
    for ($i = 0; $i < count($tokens); $i++) {
        $token = $tokens[$i];
        if (is_array($token) && $token[0] == T_STRING) {
            if ($tokens[$i + 1][0] == "(") { // Check for function call
                $functions[$token[1]][1][] = array($path, $token[2]); // Store the function reference
            }
        }
    }
}
?>

Step 3: Running the Script

  1. Update the $path variable in the script to point to your PHP project directory.
  2. Run the script through your command line or web server.
  3. The output will be an HTML table displaying all functions, how many times they’re defined, and how many times they’re referenced.

Conclusion

This custom script gives you an efficient way to track down unused functions in your PHP project. While PHP’s built-in features like Reflection and token_get_all() are powerful, having a tailored script can streamline the process of identifying and managing unused functions. As a best practice, consider running this script periodically to keep your codebase clean and efficient.

By keeping your project tidy, you not only enhance its performance but also facilitate easier maintenance and collaboration in the long term.