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 :
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();
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.
<?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.