Today, I wanted to talk about a new feature which I personally love very much. So far, Scrutinizer provided you with the ability to merge automated fixes via the command line; this is still possible and the command looks something like this:
curl https://scrutinizer-ci.com/.../inspection.diff | git apply -
However, for simple fixes like coding style issues and typos, we can do even better and make this process as smooth and fast as possible. Just like GitHub has an edit UI which allows you to propose and merge simple fixes entirely through the webinterface, Scrutinizer can now also send you a pull request for these coding style patches:
This also integrates well with the GitHub workflow. Everyone who has used GitHub knows what a pull request is, however did you know that you can send pull requests to pull requests? Let’s take a look at an example:
In this example, Bob opens a pull request on the main repository. Alice reviews this pull request. What she can do now is to comment on the pull request, and what she also can do is to actually send some fixes to Bob’s repository which he can merge into his pull request.
Scrutinizer offers you that option too. If you are in Bob’s shoes, you might want to merge the auto-fixes into your pull request. If you are the maintainer on the other hand, you might want to have Scrutinizer send the fixes directly to the main repository. Scrutinizer allows you to choose between both options.
When you abstract a general concept which can be implemented in different ways, you usually encapsulate the concept in an interface. As PHP has no type system itself however, it is still possible to call methods on an object which are not part of the interface, yet are part of the concrete implementation.
This is generally unproblematic unless someone actually wants to pass a different implementation of the interface in which case your code will break. Those errors are rarely catched by tests, but PHP Analyzer helps you to detect them early.
Let’s take a look at an example:
interface User
{
/** @return string */
public function getPassword();
}
class MyUser implements User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
There are several alternative fixes available for this issue:
Change the type-hint for the parameter:
class AuthSystem
{
public function authenticate(MyUser $user) { /* ... */ }
}
Add an additional type-check:
class AuthSystem
{
public function authenticate(User $user)
{
if ($user instanceof MyUser) {
$this->logger->info(/** ... */);
}
}
}
Add the method to the interface:
interface User
{
/** @return string */
public function getPassword();
/** @return string */
public function getDisplayName();
}
Unused code is probably one of the most basic code smells that you can have. It is code that you have needed at some point, but somewhere along the way, it became unused. In most cases, it is very easy to fix.
PHP Analyzer detects several types of unused code, and it is very deliberate about marking something as unused. If in doubt, it will not mark something to avoid generating false-positives for such a low risk issue.
This pass checks that the methods which you use are called at least once from somewhere. Also, if you use a dynamic calls, let’s say $this->$x();, PHP Analyzer will mark all the possible methods this call could reach as used.
PHP Analyzer also checks whether your parameters are used. This may sound simple, but this is actually one of the longest analyses of PHP Analyzer as we need to analyze not only your immediate code, but actually your entire type hierarchy. Let’s take a look at an example:
abstract class Command
{
public function run()
{
// ...
$rs = $this->execute($input, $output);
// ...
}
protected function execute($input, $output)
{
// Overridden in child classes.
}
}
class MyFirstCommand extends Command
{
protected function execute($input, $output)
{
$output->writeln('Doing something.');
}
}
class MySecondCommand extends Command
{
protected function execute($input, $output)
{
$x = $input->getArgument('x');
// doing something with $x.
}
}
In the above example, if we were just looking at the individual methods, we would need to label several parameters as unused:
However, in fact the execute() methods are all part of the same interface, and each parameter is used in at least one place. So, PHP Analyzer will correctly label none of these parameters as unused.
This analysis is a bit simpler again, as we just need to consider the body of a function or method:
function filteringSomething(array $groups, callable $predicate) {
$filtersGroups = array();
foreach ($groups as $group) {
if ($predicate($group) === true) {
$filterGroups[] = $group;
}
}
return $filterGroups;
}
As you can see, the first assignment to $filtersGroups (note the additional s), is not used anywhere else. For such small typos, PHP Analyzer will even suggest to correct the variable name instead of marking it as unused.
Just like for the other unused code checks, if a variable might be accessed in some dynamic fashion like ${$x} for example, PHP Analyzer will mark all the variables which might be accessed by it as used.
Scrutinizer can make certain simple fixes automatically. These simple fixes are mostly coding-style fixes like for PSR1/PSR2 and removing unused use statement (see a full list of available auto-fixes).
Sometimes you would like to avoid a certain change. This required you to apply the patch locally and then search the unwanted change and revert it manually.
To ease the usage of this feature, you can now also revert changes through the web interface very easily:
We are very excited to announce the availability of a new major version of PHP Analyzer. If you haven’t heard of PHP Analyzer yet, it is an advanced static analysis tool for PHP code; much like a compiler for PHP, except that it does not transform your source code, but analyzes it.
Before PHP Analyzer runs its bug detection routines and other heuristics, it first performs several flow analyses on your source code to get a better picture of what it does and how data is passed through it. One of these flow analyses is type inference. Our type inference engine had been growing over time as we added new features to make it more accurate. As the architecture was not planned for these new features, the speed of the engine suffered over time.
This new version contains rewritten internals which show speed improvements by 10 to 20 times or even more depending on the analyzed package. Just as an example, analyzing the latest version of the Symfony framework went down from something over an hour to a mere 3 to 4 minutes!
Since PHP Analyzer is implemented in PHP itself, and PHP is single-threaded, that inherently puts a speed limit on a CPU-intensive tool like PHP Analyzer. To still make inspections as fast as possible, the process of analyzing dependencies of your root package is distributed across several machines and done in parallel whenever possible.
These results are then cached in a persistent storage for subsequent runs and only analyzed again if their versions change. This process is now also nicely displayed in your inspection progress log:
Last but not least, this new version also comes with a couple of new checks. An in-depth view will follow over the next days and weeks, here a short summary of things that have changed:
You can enable PHP Analyzer on your project by adding the following to your configuration:
# .scrutinizer.yml
tools:
php_analyzer: true
Currently, PHP Analyzer is available for all paid plans and we also started to make it available to a few hundred of the thousands of open-source repositories on Scrutinizer. We will gradually enable it for more open-source repositories as we get a better idea of the necessary capacities.
If you’d like to get access earlier, you can join us on #scrutinizer on Freenode IRC and we might be able to squeeze you in. At any rate, if you enabled PHP Analyzer in your configuration, you will get an email once it becomes available for your repository.
Help us spread the word, and a happy new year!