1. Measuring the size of a PHP project

As Sebastian Bergmann did with phploc, we will learn how to produce Structure report of any data source.

Our goal is to create a report like the one below :

Phing 2.6.1 Structure Report
Directories                                         50
Files                                              374

Structure
  Namespaces                                         1
  Interfaces                                        15
  Traits                                             0
  Classes                                          384
    Abstract Classes                                27 (7.03%)
    Concrete Classes                               357 (92.97%)
  Methods                                         3531
    Scope
      Non-Static Methods                          3435 (97.28%)
      Static Methods                                96 (2.72%)
    Visibility
      Public Method                               3174 (89.89%)
      Protected Method                             207 (5.86%)
      Private Method                               150 (4.25%)
  Functions                                          0
    Named Functions                                  0 (0.00%)
    Anonymous Functions                              0 (0.00%)
  Constants                                        157
    Global Constants                                17 (10.83%)
    Class Constants                                140 (89.17%)

1.1. Parsing Data Source

As usual, we need to parse data source first before we can explore its elements.

<?php

use Bartlett\Reflect;
use Bartlett\Reflect\ProviderManager;
use Bartlett\Reflect\Provider\SymfonyFinderProvider;
use Symfony\Component\Finder\Finder;

$tgz = 'phar://' . __DIR__ . '/archives/phing-2.6.1.tgz';

$tgzFinder = new Finder();
$tgzFinder->files()
    ->name('*.php')
    ->in($tgz);

$pm = new ProviderManager;
$pm->set('PhingSource', new SymfonyFinderProvider($tgzFinder));

$reflect = new Reflect;
$reflect->setProviderManager($pm);
$reflect->parse();
The phar:// shema is used to parse archives formats: zip, gz, tgz, tar, and of course phar. See an example script
[https://raw.github.com/llaville/php-reflect/v2/examples/archiveContent.php]
.

Depending of the size of data source, it may be long to parse, and it will be cool to have some notification about parsing progress.

Add a listener that will echo out files when they are parsed
<?php
use Symfony\Component\EventDispatcher\GenericEvent;

$reflect->getEventDispatcher()->addListener(
    'reflect.progress',
    function (GenericEvent $e) {
        printf(
            'Parsing Data source "%s" in progress ... File "%s"' . PHP_EOL,
            $e['source'],
            $e['file']->getPathname()
        );
    }
);

1.2. Visitors

The code analyser need to know what is the contents of packages in the data source. We will explore in details using the Visitor Design Pattern implementation.

The Analyser class will extend the abstract class Bartlett\Reflect\Visitor\AbstractVisitor, that implement interface Bartlett\Reflect\Visitor\VisitorInterface.

<?php

use Bartlett\Reflect\Visitor\AbstractVisitor;

class Analyser extends AbstractVisitor
{
}

Each element that need to be explored, should have a visit method accordingly.

  • For packages, we need to implement a visitPackageModel method.

  • For classes, we need to implement a visitClassModel method.

  • For methods, we need to implement a visitMethodModel method.

  • For functions, we need to implement a visitFunctionModel method.

  • For constants, we need to implement a visitConstantModel method.

And for optional elements

  • For includes, we add an empty implementation of visitIncludeModel method.

  • For dependencies, we add an empty implementation of visitDependencyModel method.

<?php

use Bartlett\Reflect\Visitor\AbstractVisitor;

class Analyser extends AbstractVisitor
{
    public function visitPackageModel($package)
    {
    }

    public function visitClassModel($class)
    {
    }

    public function visitMethodModel($method)
    {
    }

    public function visitFunctionModel($function)
    {
    }

    public function visitConstantModel($constant)
    {
    }

    public function visitIncludeModel($include)
    {
    }

    public function visitDependencyModel($dependency)
    {
    }
}

1.3. Build the text Structure Report

Finally, after collecting all results (Analyser::__construct()), its now time to get content of the text report (Analyser::__toString()).

<?php
// ...

$analyser = new Analyser($reflect);
echo $analyser;

The full source code
[https://raw.github.com/llaville/php-reflect/v2/examples/codeAnalysis.php]
is available on GitHub repository.