Insphpect is a tool I wrote as part of my PhD project. It scans code for object-oriented programming techniques that hinder code reusability and flexibility.
Let me begin with two mundane observations:
- Business requirements change over time.
- Programmers are not clairvoyant.
New product launches, emergency lockdown regulations, expanding into new markets, economic factors, updated data protection laws: there are lots of potential causes for business software to need updating.
From those two observations we can infer that programmers know that the code they write is going to change, but not what those changes will be or when they will happen.
Writing code in such a way that it can be easily adapted is a skill that takes years to master.
You’re probably already familiar with programming practices that come back and haunt you. Novice programmers quickly realize that global variables are more trouble than they’re worth, and the once incredibly popular Singleton Pattern has been a dirty word for the last decade.
How you code your application has a big impact on how easy it is to adapt to meet new requirements. As you progress through your career, you learn techniques that make adapting code easier. Once you’ve grasped fundamentals of object-oriented programming you wonder how you ever did without it!
If you ask ten developers to produce software, given the same requirements, you’ll get ten different solutions. Some of those solutions will inevitably be better than others.
Consider a ship in a bottle and a model ship made of Lego. Both are model ships, but changing the sails on the ship in a bottle is very difficult, and reusing the parts is near impossible. However, with a Lego ship, you can easily swap out the sails or use the same components to build a model rocket, house or a car.
Certain programming techniques lead to the ship-in-a-bottle approach and make your code difficult to change and adapt.
Insphpect is a tool which scans your code for programming practices that lead to this kind of a ship in a bottle design.
It grades your code based on how flexible it is, and highlights areas where flexibility can be improved.
What does Insphpect look for?
Currently, Insphpect looks for the following:
- tight coupling
- hardcoded configuration
- setter injection
- using the
newkeyword in a constructor
- service locators
- static methods
- global state
- files that have more than one role (e.g. defining a class and running some code)
If it detects anything it identifies as inflexible, it highlights the code, explains why it highlighted the issue, then grades your whole project and individual classes on a score of 0-100 (with 100 being no issues detected). As a proof of concept, for some detections it’s able to automatically generate a patch file that re-writes the code to remove the inflexibility entirely.
Take a look a sample report here.
Insphpect is currently in the testing phase, and it would really help my research progress if you can check it out and complete the survey in the “Give your feedback” section of the site.
Are those bad practices really bad, though?
This was one of the more difficult parts of the background research, and you can read about how this was done in detail on the Insphpect website.
However, this can be summarized as:
- The opinions of each bad practice were collected from 100 authors per practice.
- The author’s opinion on the practice was graded on a scale of 1–5.
- The author’s methodological rigor was graded on a scale of 1–7 based on the Jadad score used for clinical trials.
These were then plotted like the graph below:
Each horizontal line represents an article, and the left (orange) bar for each article is the recommendation going from 5 — Avoid this practice at all costs (Far left) — to 1 — Favor this practice over alternatives.
The right (blue) bar for each article is the Jadad style score measuring analytic rigor. A score of seven means the article describes the practice, provides code examples, discusses alternative approaches, provides like-for-like code samples, discusses the pros/cons of each approach and makes a recommendation of which approach should be used.
In the case of the singleton above, authors who compare the singleton to alternative approaches, discuss the pros/cons, etc., are significantly more likely to suggest using alternative approaches.
Currently, Insphpect allows uploading code via a Git repository URL or a ZIP file.
So not to point out flaws in other people’s work, let’s take a look at one of my own projects to see what it identifies.
We’ll use https://github.com/Level-2/Transphporm as an example project.
This is quite a good example, because it has a very high score on another code-quality tool Scrutinizer.
Firstly, enter the git URL
https://github.com/Level-2/Transphporm into the text box at the top of the home page and press “Go”. It will take a few seconds to minutes, depending on the size of the project, and will generate a report that looks something like this:
Once you’re on the report page, you’ll see a summary at the top with an overall grade out of 100, with 100 being very good and 0 being very poor.
Underneath the summary, you’ll see a list of all the classes in the project, each with its own grade.
Don’t worry if your code doesn’t get a perfect score. It’s unlikely that it will. Remember, Insphpect is a tool that identifies flexibility in your code. There are parts of your code (like the entry point) where flexibility isn’t warranted.
For Transphporm, it has highlighted issues in seven classes.
Let’s take a look at some of those. Scroll down to
TransphpormParserCssToXpath and click the link. You’ll see a score for that particular class and a list of issues which have been identified.
In this case, it has identified a static variable and a static method. Clicking on one of the red lines will reveal an explanation of why the line was flagged up.
For example, clicking line 12 will give an explanation of why static variables are less flexible than instance variables.
Although there’s a more in-depth explanation of the issues caused by static properties on the report, as a quick refresher, static variables have one value which is shared across all the instances of the class.
This is inherently less flexible than an instance variable, because using an instance variable allows each instance to have a different value.
For example, consider the following:
class User public static $db; public $id; public $name; public $email; public function save() $stmt = self::$db->prepare('REPLACE INTO user (id, name, email) VALUES (:id, :name, :email)'); $stmt->execute([ 'id' => $this->id, 'name' => $this->name. 'email' => $this->email ]);
$db is static, every instance of this class shares the same
$db instance and records will always be inserted into the same database.
While this sounds reasonable, let me give you a real-world example.
How to Ensure Flexible, Reusable PHP Code with Insphpect