Repository: rskuipers/php-assumptions
Branch: master
Commit: 06591c2b7f66
Files: 28
Total size: 41.7 KB
Directory structure:
gitextract_gc4a4ste/
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│ └── phpa
├── composer.json
├── phpunit.xml.dist
├── src/
│ └── PhpAssumptions/
│ ├── Analyser.php
│ ├── Cli.php
│ ├── Detector.php
│ ├── Output/
│ │ ├── OutputInterface.php
│ │ ├── PrettyOutput.php
│ │ ├── Result.php
│ │ └── XmlOutput.php
│ └── Parser/
│ └── NodeVisitor.php
└── tests/
├── PhpAssumptions/
│ ├── AnalyserTest.php
│ ├── CliTest.php
│ ├── DetectorTest.php
│ ├── ExampleTest.php
│ ├── Output/
│ │ ├── PrettyOutputTest.php
│ │ └── XmlOutputTest.php
│ └── Parser/
│ └── NodeVisitorTest.php
├── bootstrap.php
└── fixtures/
├── Example.php
├── MyClass.php
└── MyOtherClass.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea/
vendor/
build/
composer.lock
.phpunit.result.cache
.phpunit.cache
================================================
FILE: .scrutinizer.yml
================================================
tools:
external_code_coverage: true
php_code_sniffer:
config:
standard: PSR2
checks:
php:
code_rating: true
duplication: true
================================================
FILE: .travis.yml
================================================
language: php
install:
- composer selfupdate
- composer install --prefer-source
jobs:
include:
- stage: Unit tests
script:
- vendor/bin/phpunit
php: 5.6
- stage: Unit tests
script:
- vendor/bin/phpunit
php: 7.0
- stage: Unit tests
script:
- vendor/bin/phpunit
php: 7.1
- stage: Unit tests
script:
- vendor/bin/phpunit
php: 7.2
- stage: Unit tests
script:
- vendor/bin/phpunit
php: 7.3
- stage: Code coverage
script:
- vendor/bin/phpunit
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then php ocular.phar code-coverage:upload --format=php-clover build/reports/phpunit.xml; fi
php: 7.3
================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG
List of changes since the very first version :)
## 0.9.1 - 2025-03-22
- Support nikic/php-parser:5.4
- Update to PHPUnit 11
## 0.9.0 - 2025-03-20
- Support nikic/php-parser:5.0
## 0.8.1 - 2022-02-15
- Fix deprecated error
## 0.7.0 - 2018-03-01
- Added an option to exclude directories
## 0.5.0 - 2016-11-15
- Add PHP 7.0 support and drop 5.5 support
## 0.4.0 - 2015-08-23
- Add metrics: "x out of y bool expressions are assumptions (z%)"
## 0.3.0 - 2015-08-15
- Add XML output (added -f and -o arguments)
- Migrated PhpSpec tests to PHPUnit
- Pretty format now outputs a table
## 0.2.0 - 2015-08-10
- Added more weak assumptions detections
## 0.1.0 - 2015-08-05
- Initial release
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Rick Kuipers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# PHP Assumptions
[](https://scrutinizer-ci.com/g/rskuipers/php-assumptions/build-status/master)
[](https://scrutinizer-ci.com/g/rskuipers/php-assumptions/?branch=master)
[](https://scrutinizer-ci.com/g/rskuipers/php-assumptions/?branch=master)
## Setup
```sh
$ composer require --dev rskuipers/php-assumptions
```
## Introduction
PHP Assumptions is the result of a proof of concept inspired by the "[From assumptions to assertions](http://rskuipers.com/entry/from-assumptions-to-assertions)" blog post.
It's a static code analysis tool doing checks for weak assumptions.
This is an example of an **assumption**:
```php
if ($user !== null) {
$user->logout();
}
```
Running `bin/phpa` on this file would yield the following output:
```
----------------------------------------------
| file | line | message |
==============================================
| example.php | 3 | if ($user !== null) { |
----------------------------------------------
1 out of 1 boolean expressions are assumptions (100%)
```
This is an example of an **assertion**:
```php
if ($user instanceof User) {
$user->logout();
}
```
## Tests
This project is built with [PHPUnit](https://github.com/sebastianbergmann/phpunit) and [Prophecy](https://github.com/phpspec/prophecy-phpunit).
In order to run these tests make sure you have dev dependencies installed with composer.
Running PHPUnit:
```sh
$ ./vendor/bin/phpunit
```
================================================
FILE: bin/phpa
================================================
#!/usr/bin/env php
<?php
$autoloaders = [__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'];
foreach ($autoloaders as $autoloader) {
if (is_file($autoloader)) {
require_once $autoloader;
break;
}
}
ini_set('xdebug.max_nesting_level', 3000);
$cli = new \PhpAssumptions\Cli(new \League\CLImate\CLImate());
exit($cli->handle($argv));
================================================
FILE: composer.json
================================================
{
"name": "rskuipers/php-assumptions",
"description": "Static code analysis tool to detect weak assumptions",
"license": "MIT",
"authors": [
{
"name": "Rick Kuipers",
"email": "io@rskuipers.com"
}
],
"require": {
"nikic/php-parser": "^4.0|^5.0",
"league/climate": "^3.1"
},
"require-dev": {
"phpunit/phpunit": "^11.5",
"phpspec/prophecy-phpunit": "^2.3"
},
"autoload": {
"psr-4": {
"PhpAssumptions\\": "src/PhpAssumptions"
}
},
"bin": ["bin/phpa"]
}
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd" colors="true" bootstrap="tests/bootstrap.php" cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="default">
<directory>tests/</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>src/</directory>
</include>
</source>
</phpunit>
================================================
FILE: src/PhpAssumptions/Analyser.php
================================================
<?php
namespace PhpAssumptions;
use PhpAssumptions\Output\Result;
use PhpParser\Node;
use PhpParser\NodeTraverserInterface;
use PhpParser\Parser;
class Analyser
{
/**
* @var Parser
*/
private $parser;
/**
* @var NodeTraverserInterface
*/
private $traverser;
/**
* @var string
*/
private $currentFilePath;
/**
* @var array
*/
private $currentFile = [];
/**
* @var Result
*/
private $result;
/**
* @var array|\string[]
*/
private $excludes = [];
/**
* @param Parser $parser
* @param NodeTraverserInterface $nodeTraverser
* @param string[] $excludes
*/
public function __construct(
Parser $parser,
NodeTraverserInterface $nodeTraverser,
$excludes = []
) {
$this->parser = $parser;
$this->traverser = $nodeTraverser;
$this->result = new Result();
$this->excludes = $excludes;
}
/**
* @param array $files
* @return Result
*/
public function analyse(array $files)
{
foreach ($files as $file) {
if (in_array($file, $this->excludes, true)) {
continue;
}
$this->currentFilePath = $file;
$this->currentFile = [];
$statements = $this->parser->parse(file_get_contents($file));
if (is_array($statements) || $statements instanceof Node) {
$this->traverser->traverse($statements);
}
}
return $this->result;
}
/**
* @param int $line
*/
public function foundAssumption($line)
{
$this->result->addAssumption($this->currentFilePath, $line, $this->readLine($line));
}
public function foundBoolExpression()
{
$this->result->increaseBoolExpressionsCount();
}
private function readLine($line)
{
if (count($this->currentFile) === 0) {
$this->currentFile = file($this->currentFilePath);
}
return trim($this->currentFile[$line - 1]);
}
}
================================================
FILE: src/PhpAssumptions/Cli.php
================================================
<?php
namespace PhpAssumptions;
use League\CLImate\CLImate;
use PhpAssumptions\Output\PrettyOutput;
use PhpAssumptions\Output\XmlOutput;
use PhpAssumptions\Parser\NodeVisitor;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
class Cli
{
const VERSION = '0.9.1';
/**
* @var CLImate
*/
private $cli;
/**
* @var \PhpParser\Parser
*/
private $parser;
private function createParser()
{
$parser = (new ParserFactory)->createForNewestSupportedVersion();
return $parser;
}
public function __construct(CLImate $cli)
{
$this->cli = $cli;
$this->cli->arguments->add(
[
'path' => [
'description' => 'The path to analyse',
'required' => false,
],
'format' => [
'prefix' => 'f',
'longPrefix' => 'format',
'description' => 'Format (pretty, xml)',
'defaultValue' => 'pretty',
],
'exclude' => [
'prefix' => 'e',
'longPrefix' => 'exclude',
'description' => 'List of files/directories (separate by ",") to exclude from the analyse',
'defaultValue' => ''
],
'output' => [
'prefix' => 'o',
'longPrefix' => 'output',
'description' => 'Output file',
'defaultValue' => 'phpa.xml',
],
'version' => [
'longPrefix' => 'version',
'description' => 'Show the version'
],
]
);
$this->parser = self::createParser();
}
/**
* @param array $args
* @return int
*/
public function handle(array $args)
{
try {
$this->cli->arguments->parse($args);
} catch (\Exception $e) {
$this->cli->usage($args);
return 100;
}
if ($this->cli->arguments->defined('version')) {
$this->cli->out(Cli::VERSION);
return 0;
}
$this->cli->out(sprintf('PHPAssumptions analyser v%s by @rskuipers', Cli::VERSION))->br();
$target = $this->cli->arguments->get('path');
if (!is_string($target)) {
$this->cli->error('Missing target path')->br();
$this->cli->usage($args);
return 100;
}
switch ($this->cli->arguments->get('format')) {
case 'xml':
$output = new XmlOutput($this->cli, $this->cli->arguments->get('output'));
break;
default:
$output = new PrettyOutput($this->cli);
break;
}
$excludes = $this->getPathsFromList((string) $this->cli->arguments->get('exclude'));
$nodeTraverser = new NodeTraverser();
$analyser = new Analyser(
$this->parser,
$nodeTraverser,
$excludes
);
$nodeTraverser->addVisitor(new NodeVisitor($analyser, new Detector()));
$target = $this->cli->arguments->get('path');
$targets = $this->getPaths($target);
$result = $analyser->analyse($targets);
$output->output($result);
if ($result->getAssumptionsCount() > 0) {
return 110;
}
return 0;
}
/**
* @param string $list
* @return array
*/
private function getPathsFromList($list)
{
$paths = [];
if ($list !== '') {
$items = explode(',', $list);
foreach ($items as $item) {
$paths = array_merge($paths, $this->getPaths($item));
}
}
return $paths;
}
/**
* @param string $fromPath
* @return array
*/
private function getPaths($fromPath)
{
$paths = [];
if (is_file($fromPath)) {
$paths[] = $fromPath;
} else {
$directory = new \RecursiveDirectoryIterator($fromPath);
$iterator = new \RecursiveIteratorIterator($directory);
$regex = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
foreach ($regex as $file) {
$paths[] = $file[0];
}
}
return $paths;
}
}
================================================
FILE: src/PhpAssumptions/Detector.php
================================================
<?php
namespace PhpAssumptions;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
class Detector
{
/**
* @param Node $node
* @return bool
*/
public function scan(Node $node)
{
if (($node instanceof Stmt\Expression)) {
$node = $node->expr;
}
if (($node instanceof Expr\BinaryOp\BooleanOr || $node instanceof Expr\BinaryOp\BooleanAnd)
&& $this->bidirectionalCheck($node, Expr\Variable::class, Expr\BinaryOp::class)
) {
return true;
}
if ($node instanceof Expr\BinaryOp\Equal || $node instanceof Expr\BinaryOp\NotEqual
|| $node instanceof Expr\BinaryOp\NotIdentical
) {
return true;
}
if ($this->isVariableExpression($node)) {
return true;
}
return false;
}
/**
* @param Node $node
* @return bool
*/
public function isBoolExpression(Node $node)
{
if ($node instanceof Expr\Ternary || $node instanceof Stmt\If_
|| $node instanceof Stmt\ElseIf_ || $node instanceof Stmt\While_
|| $node instanceof Expr\BinaryOp\BooleanAnd || $node instanceof Expr\BinaryOp\BooleanOr
|| $node instanceof Stmt\For_
) {
return true;
}
return false;
}
/**
* @param Node $node
* @return bool
*/
private function isVariableExpression(Node $node)
{
if ($node instanceof Expr\BooleanNot && $node->expr instanceof Expr\Variable) {
return true;
}
if ($node instanceof Expr\Ternary || $node instanceof Stmt\If_
|| $node instanceof Stmt\ElseIf_ || $node instanceof Stmt\While_
|| $node instanceof Stmt\For_
) {
if ($node->cond instanceof Expr\Variable) {
return true;
}
if (is_array($node->cond)) {
foreach ($node->cond as $condition) {
if ($condition instanceof Expr\Variable) {
return true;
}
}
}
}
return false;
}
/**
* @param Expr $condition
* @param string $left
* @param string $right
* @return bool
*/
private function bidirectionalCheck(Expr $condition, $left, $right)
{
return ($this->isInstanceOf($condition->left, $left) && $this->isInstanceOf($condition->right, $right))
|| ($this->isInstanceOf($condition->right, $left) && $this->isInstanceOf($condition->left, $right));
}
/**
* @param object $object
* @param string $class
* @return bool
*/
private function isInstanceOf($object, $class)
{
return get_class($object) === $class || is_subclass_of($object, $class);
}
}
================================================
FILE: src/PhpAssumptions/Output/OutputInterface.php
================================================
<?php
namespace PhpAssumptions\Output;
interface OutputInterface
{
/**
* @param Result $result
*/
public function output(Result $result);
}
================================================
FILE: src/PhpAssumptions/Output/PrettyOutput.php
================================================
<?php
namespace PhpAssumptions\Output;
use League\CLImate\CLImate;
class PrettyOutput implements OutputInterface
{
/**
* @var CLImate
*/
private $cli;
/**
* @param CLImate $cli
*/
public function __construct(CLImate $cli)
{
$this->cli = $cli;
}
/**
* @param Result $result
*/
public function output(Result $result)
{
if ($result->getAssumptionsCount() > 0) {
$this->cli->table($result->getAssumptions())->br();
}
$this->cli->out(
sprintf(
'%d out of %d boolean expressions are assumptions (%d%%)',
$result->getAssumptionsCount(),
$result->getBoolExpressionsCount(),
$result->getPercentage()
)
);
}
}
================================================
FILE: src/PhpAssumptions/Output/Result.php
================================================
<?php
namespace PhpAssumptions\Output;
class Result
{
/**
* @var array
*/
private $assumptions = [];
/**
* @var int
*/
private $boolExpressionsCount = 0;
/**
* @param string $file
* @param int $line
* @param string $message
*/
public function addAssumption($file, $line, $message)
{
$this->assumptions[] = [
'file' => $file,
'line' => $line,
'message' => $message,
];
}
public function increaseBoolExpressionsCount()
{
$this->boolExpressionsCount++;
}
/**
* @return array
*/
public function getAssumptions()
{
return $this->assumptions;
}
/**
* @return int
*/
public function getAssumptionsCount()
{
return count($this->assumptions);
}
/**
* @return float
*/
public function getPercentage()
{
if ($this->getBoolExpressionsCount() === 0) {
return 0;
}
return round($this->getAssumptionsCount() / $this->getBoolExpressionsCount() * 100);
}
/**
* @return int
*/
public function getBoolExpressionsCount()
{
return $this->boolExpressionsCount;
}
}
================================================
FILE: src/PhpAssumptions/Output/XmlOutput.php
================================================
<?php
namespace PhpAssumptions\Output;
use League\CLImate\CLImate;
use PhpAssumptions\Cli;
class XmlOutput implements OutputInterface
{
/**
* @var \DOMDocument
*/
private $document;
/**
* @var string
*/
private $file;
/**
* @var \DOMXPath
*/
private $xpath;
/**
* @var CLImate
*/
private $cli;
/**
* @param CLImate $cli
* @param string $file
*/
public function __construct(CLImate $cli, $file)
{
$this->cli = $cli;
$this->file = $file;
$this->document = new \DOMDocument();
$phpaNode = $this->document->createElement('phpa');
$phpaNode->setAttribute('version', Cli::VERSION);
$this->document->appendChild($phpaNode);
$filesNode = $this->document->createElement('files');
$phpaNode->appendChild($filesNode);
$this->xpath = new \DOMXPath($this->document);
}
/**
* @param Result $result
*/
public function output(Result $result)
{
$assumptions = $result->getAssumptions();
foreach ($assumptions as $assumption) {
$fileElements = $this->xpath->query('/phpa/files/file[@name="' . $assumption['file'] . '"]');
if ($fileElements->length === 0) {
$files = $this->xpath->query('/phpa/files')->item(0);
$fileElement = $this->document->createElement('file');
$fileElement->setAttribute('name', $assumption['file']);
$files->appendChild($fileElement);
} else {
$fileElement = $fileElements->item(0);
}
$lineElement = $this->document->createElement('line');
$lineElement->setAttribute('number', $assumption['line']);
$lineElement->setAttribute('message', $assumption['message']);
$fileElement->appendChild($lineElement);
}
$this->document->documentElement->setAttribute('assumptions', $result->getAssumptionsCount());
$this->document->documentElement->setAttribute('bool-expressions', $result->getBoolExpressionsCount());
$this->document->documentElement->setAttribute('percentage', $result->getPercentage());
$this->document->preserveWhiteSpace = false;
$this->document->formatOutput = true;
$this->document->save($this->file);
$this->cli->out(sprintf('Written %d assumption(s) to file %s', $result->getAssumptionsCount(), $this->file));
}
}
================================================
FILE: src/PhpAssumptions/Parser/NodeVisitor.php
================================================
<?php
namespace PhpAssumptions\Parser;
use PhpAssumptions\Analyser;
use PhpAssumptions\Detector;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
class NodeVisitor extends NodeVisitorAbstract
{
/**
* @var Analyser
*/
private $analyser;
/**
* @var Detector
*/
private $detector;
/**
* @param Analyser $analyser
* @param Detector $detector
*/
public function __construct(Analyser $analyser, Detector $detector)
{
$this->analyser = $analyser;
$this->detector = $detector;
}
/**
* @param Node $node
* @return false|null|Node|\PhpParser\Node[]|void
*/
public function enterNode(Node $node)
{
if ($this->detector->isBoolExpression($node)) {
$this->analyser->foundBoolExpression();
}
if ($this->detector->scan($node)) {
$this->analyser->foundAssumption($node->getLine());
}
}
}
================================================
FILE: tests/PhpAssumptions/AnalyserTest.php
================================================
<?php
namespace tests\PhpAssumptions;
use PhpAssumptions\Analyser;
use PhpAssumptions\Output\OutputInterface;
use PhpParser\Parser;
use PhpParser\ParserAbstract;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
class AnalyserTest extends TestCase
{
use ProphecyTrait;
/**
* @var Parser
*/
private $parser;
/**
* @var OutputInterface
*/
private $output;
/**
* @var NodeTraverser
*/
private $nodeTraverser;
/**
* @var Analyser
*/
private $analyser;
/**
* @var Node
*/
private $node;
public function setUp(): void
{
$this->node = $this->prophesize(Node::class);
$this->parser = $this->prophesize(ParserAbstract::class);
$this->output = $this->prophesize(OutputInterface::class);
$this->nodeTraverser = $this->prophesize(NodeTraverser::class);
$this->analyser = new Analyser(
$this->parser->reveal(),
$this->nodeTraverser->reveal(),
[fixture('MyOtherClass.php')]
);
}
#[Test]
public function itShouldAnalyseAllFiles()
{
$files = [fixture('MyClass.php')];
$nodes = [$this->node];
$this->parser->parse(Argument::type('string'))->shouldBeCalled()->willReturn($nodes);
$this->nodeTraverser->traverse($nodes)->shouldBeCalled();
$this->analyser->analyse($files);
}
#[Test]
public function itShouldIgnoreExcludeFiles()
{
$files = [fixture('MyClass.php'), fixture('MyOtherClass.php')];
$nodes = [$this->node];
$this->parser->parse(Argument::type('string'))->shouldBeCalled()->willReturn($nodes);
$this->nodeTraverser->traverse($nodes)->shouldBeCalled();
$this->analyser->analyse($files);
}
}
================================================
FILE: tests/PhpAssumptions/CliTest.php
================================================
<?php
namespace tests\PhpAssumptions;
use League\CLImate\Argument\Manager;
use League\CLImate\CLImate;
use PhpAssumptions\Cli;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
class CliTest extends TestCase
{
use ProphecyTrait;
/**
* @var Cli
*/
private $cli;
/**
* @var CLImate
*/
private $climate;
/**
* @var Manager
*/
private $argumentManager;
public function setUp(): void
{
$this->argumentManager = $this->prophesize(Manager::class);
$this->argumentManager->add(Argument::type('array'))->shouldBeCalled();
$this->argumentManager->parse(Argument::type('array'))->shouldBeCalled();
$this->climate = $this->prophesize(CLImate::class);
$this->climate->arguments = $this->argumentManager->reveal();
$this->cli = new Cli($this->climate->reveal());
}
#[Test]
public function itShouldAnalyseTargetFile()
{
$this->itShouldShowAuthor();
$path = fixture('MyClass.php');
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('pretty');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn($path);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn('');
$this->climate->table([
[
'file' => $path,
'line' => 9,
'message' => 'if ($dog !== null) {',
]
])->shouldBeCalled()->willReturn($this->climate);
$this->climate->out('1 out of 2 boolean expressions are assumptions (50%)')->shouldBeCalled();
$this->cli->handle(['phpa', $path]);
}
#[Test]
public function itShouldAnalyseTargetDirectory()
{
$this->itShouldShowAuthor();
$files = [fixture('MyClass.php'), fixture('MyOtherClass.php'), fixture('Example.php')];
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('pretty');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn(FIXTURES_DIR);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn('');
// Assert that all files show up in the table
$this->climate->table(Argument::that(function ($table) use ($files) {
foreach ($table as $row) {
unset($files[array_search($row['file'], $files)]);
}
return count($files) === 0;
}))->shouldBeCalled()->willReturn($this->climate);
$this->climate->out(Argument::containingString('boolean expressions are assumptions'))->shouldBeCalled();
$this->cli->handle(['phpa', FIXTURES_DIR]);
}
#[Test]
public function itShouldIgnoreExcludeFile()
{
$this->itShouldShowAuthor();
$path = fixture('MyClass.php');
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('pretty');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn($path);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn(fixture('MyClass.php'));
$this->climate->table()->shouldNotBeCalled();
$this->climate->out('0 out of 0 boolean expressions are assumptions (0%)')->shouldBeCalled();
$this->cli->handle(['phpa', $path]);
}
#[Test]
public function itShouldIgnoreExcludeFileFromDirectory()
{
$this->itShouldShowAuthor();
$path = fixture('MyClass.php');
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('pretty');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn(FIXTURES_DIR);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn(
fixture('MyOtherClass.php') . ',' . fixture('Example.php')
);
$this->climate->table([
[
'file' => $path,
'line' => 9,
'message' => 'if ($dog !== null) {',
]
])->shouldBeCalled()->willReturn($this->climate);
$this->climate->out('1 out of 2 boolean expressions are assumptions (50%)')->shouldBeCalled();
$this->cli->handle(['phpa', FIXTURES_DIR]);
}
#[Test]
public function itShouldIgnoreExcludeDirectory()
{
$this->itShouldShowAuthor();
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('pretty');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn(FIXTURES_DIR);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn(fixture(''));
// Assert that all files show up in the table
$this->climate->table()->shouldNotBeCalled();
$this->climate->out(Argument::containingString('boolean expressions are assumptions'))->shouldBeCalled();
$this->cli->handle(['phpa', FIXTURES_DIR]);
}
#[Test]
public function itShouldAnalyseTargetFileAndOutputXml()
{
$this->itShouldShowAuthor();
$path = fixture('MyClass.php');
$output = tempnam(sys_get_temp_dir(), 'xml');
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(false);
$this->argumentManager->get('format')->shouldBeCalled()->willReturn('xml');
$this->argumentManager->get('path')->shouldBeCalled()->willReturn($path);
$this->argumentManager->get('output')->shouldBeCalled()->willReturn($output);
$this->argumentManager->get('exclude')->shouldBeCalled()->willReturn('');
$this->climate->out('Written 1 assumption(s) to file ' . $output)->shouldBeCalled();
$this->cli->handle(['phpa', $path]);
$this->assertTrue(is_file($output));
}
#[Test]
public function itShouldShowUsageWithNoArgs()
{
$this->argumentManager->parse(Argument::type('array'))->willThrow(\Exception::class);
$args = ['phpa'];
$this->climate->usage($args)->shouldBeCalled();
$this->cli->handle($args);
}
#[Test]
public function itShouldShowVersion()
{
$this->argumentManager->defined('version')->shouldBeCalled()->willReturn(true);
$args = ['phpa', '--version'];
$this->climate->out(Cli::VERSION)->shouldBeCalled();
$this->cli->handle($args);
}
private function itShouldShowAuthor()
{
$this->climate->out(Argument::containingString('PHPAssumptions analyser'))
->shouldBeCalled()
->willReturn($this->climate);
$this->climate->br()->shouldBeCalled();
}
}
================================================
FILE: tests/PhpAssumptions/DetectorTest.php
================================================
<?php
namespace tests\PhpAssumptions;
use PhpAssumptions\Detector;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
class DetectorTest extends TestCase
{
use ProphecyTrait;
/**
* @var Parser
*/
private $parser;
/**
* @var Detector
*/
private $detector;
public function setUp(): void
{
$this->parser = (new ParserFactory)->createForNewestSupportedVersion();
$this->detector = new Detector();
}
#[Test]
public function itShouldDetectNotNull()
{
$node = $this->parser->parse('<?php $var !== null;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php null !== $var;')[0];
$this->assertTrue($this->detector->scan($node));
}
#[Test]
public function itShouldDetectEqualsNotFalse()
{
$node = $this->parser->parse('<?php $test !== false;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php false !== $test;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php false != $test;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php $test != false;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php !$test;')[0];
$this->assertTrue($this->detector->scan($node));
}
#[Test]
public function itShouldDetectEqualsTrue()
{
$node = $this->parser->parse('<?php $test !== true;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php true !== $test;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php true != $test;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php $test != true;')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php $test ? "" : "";')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php if ($test instanceof Test) { } elseif ($test) { }')[0]->elseifs[0];
$this->assertTrue($this->detector->scan($node));
}
#[Test]
public function itShouldDetectEqualsScalar()
{
$node = $this->parser->parse('<?php $test == "test";')[0];
$this->assertTrue($this->detector->scan($node));
$node = $this->parser->parse('<?php "test" == $test;')[0];
$this->assertTrue($this->detector->scan($node));
}
#[Test]
public function itShouldDetectWhileAssumptions()
{
$node = $this->parser->parse('<?php while ($test);')[0];
$this->assertTrue($this->detector->scan($node));
}
#[Test]
public function itShouldDetectForAssumptions()
{
$node = $this->parser->parse('<?php for ($i = 0; $i; $i++);')[0];
$this->assertTrue($this->detector->scan($node));
}
}
================================================
FILE: tests/PhpAssumptions/ExampleTest.php
================================================
<?php
namespace tests;
use PhpAssumptions\Analyser;
use PhpAssumptions\Detector;
use PhpAssumptions\Parser\NodeVisitor;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
class ExampleTest extends TestCase
{
use ProphecyTrait;
/**
* @var Analyser
*/
private $analyser;
public function setUp(): void
{
$nodeTraverser = new NodeTraverser();
$this->analyser = new Analyser((new ParserFactory)->createForNewestSupportedVersion(), $nodeTraverser);
$nodeTraverser->addVisitor(new NodeVisitor($this->analyser, new Detector()));
}
#[Test]
public function itShouldProperlyDetectAssumptions()
{
$file = fixture('Example.php');
$result = $this->analyser->analyse([$file]);
$this->assertSame([
[
'file' => $file,
'line' => 9,
'message' => '$test = $bla && $bla === \'test\' || $bla === \'ha\' ? \'haha\' : \'test\';',
],
[
'file' => $file,
'line' => 10,
'message' => 'if ($test && $test > 0) {',
],
[
'file' => $file,
'line' => 12,
'message' => '} elseif (!$test) {',
],
[
'file' => $file,
'line' => 19,
'message' => 'while ($test) {',
],
[
'file' => $file,
'line' => 23,
'message' => 'for ($i = 0; $i; $i++) {',
],
], $result->getAssumptions());
}
#[Test]
public function itShouldProperlyDetectBoolExpressions()
{
$file = fixture('Example.php');
$result = $this->analyser->analyse([$file]);
$this->assertEquals(12, $result->getBoolExpressionsCount());
}
}
================================================
FILE: tests/PhpAssumptions/Output/PrettyOutputTest.php
================================================
<?php
namespace tests\PhpAssumptions\Output;
use League\CLImate\CLImate;
use PhpAssumptions\Output\PrettyOutput;
use PhpAssumptions\Output\Result;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
class PrettyOutputTest extends TestCase
{
use ProphecyTrait;
/**
* @var PrettyOutput
*/
private $output;
/**
* @var CLImate
*/
private $climate;
/**
* @var Result
*/
private $result;
public function setUp(): void
{
$this->climate = $this->prophesize(CLImate::class);
$this->result = $this->prophesize(Result::class);
$this->output = new PrettyOutput($this->climate->reveal());
}
#[Test]
public function itShouldOutputWhatIsWritten()
{
$assumptions = [
[
'file' => 'MyClass.php',
'line' => 120,
'message' => '$test',
]
];
$this->result->getAssumptions()->shouldBeCalled()->willReturn($assumptions);
$this->result->getAssumptionsCount()->shouldBeCalled()->willReturn(1);
$this->result->getBoolExpressionsCount()->shouldBeCalled()->willReturn(3);
$this->result->getPercentage()->shouldBeCalled()->willReturn(33.3333333);
$this->climate->table($assumptions)->shouldBeCalled()->willReturn($this->climate);
$this->climate->br()->shouldBeCalled();
$this->climate->out('1 out of 3 boolean expressions are assumptions (33%)')->shouldBeCalled();
$this->output->output($this->result->reveal());
}
}
================================================
FILE: tests/PhpAssumptions/Output/XmlOutputTest.php
================================================
<?php
namespace tests\PhpAssumptions\Output;
use League\CLImate\CLImate;
use PhpAssumptions\Cli;
use PhpAssumptions\Output\Result;
use PhpAssumptions\Output\XmlOutput;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
class XmlOutputTest extends TestCase
{
use ProphecyTrait;
/**
* @var XmlOutput
*/
private $xmlOutput;
/**
* @var CLImate
*/
private $cli;
/**
* @var Result
*/
private $result;
/**
* @var string
*/
private $file;
public function setUp(): void
{
$this->file = tempnam(sys_get_temp_dir(), 'xml');
$this->cli = $this->prophesize(CLImate::class);
$this->result = $this->prophesize(Result::class);
$this->xmlOutput = new XmlOutput($this->cli->reveal(), $this->file);
}
#[Test]
public function itShouldGenerateValidXml()
{
$this->result->getAssumptions()->shouldBeCalled()->willReturn([
[
'file' => 'MyClass.php',
'line' => 122,
'message' => 'if ($test) {'
],
[
'file' => 'MyClass.php',
'line' => 132,
'message' => '$test ? "Yes" : "No"'
],
[
'file' => 'MyOtherClass.php',
'line' => 12,
'message' => 'if ($test !== false) {'
]
]);
$this->result->getAssumptionsCount()->shouldBeCalled()->willReturn(3);
$this->result->getPercentage()->shouldBeCalled()->willReturn(60);
$this->result->getBoolExpressionsCount()->shouldBeCalled()->willReturn(5);
$this->xmlOutput->output($this->result->reveal());
$version = Cli::VERSION;
$expected = <<<XML
<?xml version="1.0"?>
<phpa version="{$version}" assumptions="3" bool-expressions="5" percentage="60">
<files>
<file name="MyClass.php">
<line number="122" message="if (\$test) {" />
<line number="132" message="\$test ? "Yes" : "No"" />
</file>
<file name="MyOtherClass.php">
<line number="12" message="if (\$test !== false) {" />
</file>
</files>
</phpa>
XML;
$this->assertXmlStringEqualsXmlString($expected, file_get_contents($this->file));
}
}
================================================
FILE: tests/PhpAssumptions/Parser/NodeVisitorTest.php
================================================
<?php
namespace tests\PhpAssumptions\Parser;
use PhpAssumptions\Analyser;
use PhpAssumptions\Detector;
use PhpAssumptions\Parser\NodeVisitor;
use PhpParser\Node;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
class NodeVisitorTest extends TestCase
{
use ProphecyTrait;
/**
* @var NodeVisitor
*/
private $nodeVisitor;
/**
* @var Analyser
*/
private $analyser;
/**
* @var Detector
*/
private $detector;
/**
* @var Node
*/
private $node;
public function setUp(): void
{
$this->analyser = $this->prophesize(Analyser::class);
$this->detector = $this->prophesize(Detector::class);
$this->node = $this->prophesize(Node::class);
$this->nodeVisitor = new NodeVisitor(
$this->analyser->reveal(),
$this->detector->reveal()
);
}
#[Test]
public function itShouldCallScanAndWriteOnSuccess()
{
$this->node->getLine()->shouldBeCalled()->willReturn(120);
$this->detector->scan($this->node)->shouldBeCalled()->willReturn(true);
$this->detector->isBoolExpression($this->node)->shouldBeCalled()->willReturn(true);
$this->analyser->foundAssumption(120)->shouldBeCalled();
$this->analyser->foundBoolExpression()->shouldBeCalled();
$this->nodeVisitor->enterNode($this->node->reveal());
}
#[Test]
public function itShouldCallScanAndNotWriteOnFailure()
{
$this->detector->scan($this->node)->shouldBeCalled()->willReturn(false);
$this->detector->isBoolExpression($this->node)->shouldBeCalled()->willReturn(true);
$this->analyser->foundAssumption(Argument::any(), Argument::any())->shouldNotBeCalled();
$this->analyser->foundBoolExpression()->shouldBeCalled();
$this->nodeVisitor->enterNode($this->node->reveal());
}
}
================================================
FILE: tests/bootstrap.php
================================================
<?php
require __DIR__ . '/../vendor/autoload.php';
define('FIXTURES_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'fixtures');
function fixture($filename)
{
return FIXTURES_DIR . DIRECTORY_SEPARATOR . $filename;
}
================================================
FILE: tests/fixtures/Example.php
================================================
<?php
namespace Test;
class Example
{
public function run($bla)
{
$test = $bla && $bla === 'test' || $bla === 'ha' ? 'haha' : 'test';
if ($test && $test > 0) {
echo '';
} elseif (!$test) {
echo '';
} else {
echo '';
}
$test = true;
while ($test) {
$test = false;
}
for ($i = 0; $i; $i++) {
echo '';
}
switch ($test) {
default:
echo '';
break;
}
if (empty($test)) {
echo '';
}
if (!is_null($test)) {
echo '';
}
if (fixture($test)) {
echo '';
}
if ($test === 'hi') {
echo '';
}
}
}
================================================
FILE: tests/fixtures/MyClass.php
================================================
<?php
namespace tests\fixtures;
class MyClass
{
public function run($dog, $cat)
{
if ($dog !== null) {
$dog->woof();
}
if ($cat instanceof MyOtherClass) {
$cat->run($dog);
}
}
}
================================================
FILE: tests/fixtures/MyOtherClass.php
================================================
<?php
namespace tests\fixtures;
class MyOtherClass
{
public function run($cat)
{
if ($cat !== null) {
$cat->meow();
}
}
}
gitextract_gc4a4ste/
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│ └── phpa
├── composer.json
├── phpunit.xml.dist
├── src/
│ └── PhpAssumptions/
│ ├── Analyser.php
│ ├── Cli.php
│ ├── Detector.php
│ ├── Output/
│ │ ├── OutputInterface.php
│ │ ├── PrettyOutput.php
│ │ ├── Result.php
│ │ └── XmlOutput.php
│ └── Parser/
│ └── NodeVisitor.php
└── tests/
├── PhpAssumptions/
│ ├── AnalyserTest.php
│ ├── CliTest.php
│ ├── DetectorTest.php
│ ├── ExampleTest.php
│ ├── Output/
│ │ ├── PrettyOutputTest.php
│ │ └── XmlOutputTest.php
│ └── Parser/
│ └── NodeVisitorTest.php
├── bootstrap.php
└── fixtures/
├── Example.php
├── MyClass.php
└── MyOtherClass.php
SYMBOL INDEX (80 symbols across 19 files)
FILE: src/PhpAssumptions/Analyser.php
class Analyser (line 10) | class Analyser
method __construct (line 47) | public function __construct(
method analyse (line 62) | public function analyse(array $files)
method foundAssumption (line 82) | public function foundAssumption($line)
method foundBoolExpression (line 87) | public function foundBoolExpression()
method readLine (line 92) | private function readLine($line)
FILE: src/PhpAssumptions/Cli.php
class Cli (line 12) | class Cli
method createParser (line 26) | private function createParser()
method __construct (line 32) | public function __construct(CLImate $cli)
method handle (line 72) | public function handle(array $args)
method getPathsFromList (line 135) | private function getPathsFromList($list)
method getPaths (line 152) | private function getPaths($fromPath)
FILE: src/PhpAssumptions/Detector.php
class Detector (line 9) | class Detector
method scan (line 15) | public function scan(Node $node)
method isBoolExpression (line 44) | public function isBoolExpression(Node $node)
method isVariableExpression (line 61) | private function isVariableExpression(Node $node)
method bidirectionalCheck (line 93) | private function bidirectionalCheck(Expr $condition, $left, $right)
method isInstanceOf (line 104) | private function isInstanceOf($object, $class)
FILE: src/PhpAssumptions/Output/OutputInterface.php
type OutputInterface (line 5) | interface OutputInterface
method output (line 10) | public function output(Result $result);
FILE: src/PhpAssumptions/Output/PrettyOutput.php
class PrettyOutput (line 7) | class PrettyOutput implements OutputInterface
method __construct (line 17) | public function __construct(CLImate $cli)
method output (line 25) | public function output(Result $result)
FILE: src/PhpAssumptions/Output/Result.php
class Result (line 5) | class Result
method addAssumption (line 22) | public function addAssumption($file, $line, $message)
method increaseBoolExpressionsCount (line 31) | public function increaseBoolExpressionsCount()
method getAssumptions (line 39) | public function getAssumptions()
method getAssumptionsCount (line 47) | public function getAssumptionsCount()
method getPercentage (line 55) | public function getPercentage()
method getBoolExpressionsCount (line 67) | public function getBoolExpressionsCount()
FILE: src/PhpAssumptions/Output/XmlOutput.php
class XmlOutput (line 8) | class XmlOutput implements OutputInterface
method __construct (line 34) | public function __construct(CLImate $cli, $file)
method output (line 53) | public function output(Result $result)
FILE: src/PhpAssumptions/Parser/NodeVisitor.php
class NodeVisitor (line 10) | class NodeVisitor extends NodeVisitorAbstract
method __construct (line 26) | public function __construct(Analyser $analyser, Detector $detector)
method enterNode (line 36) | public function enterNode(Node $node)
FILE: tests/PhpAssumptions/AnalyserTest.php
class AnalyserTest (line 16) | class AnalyserTest extends TestCase
method setUp (line 45) | public function setUp(): void
method itShouldAnalyseAllFiles (line 58) | #[Test]
method itShouldIgnoreExcludeFiles (line 71) | #[Test]
FILE: tests/PhpAssumptions/CliTest.php
class CliTest (line 13) | class CliTest extends TestCase
method setUp (line 32) | public function setUp(): void
method itShouldAnalyseTargetFile (line 45) | #[Test]
method itShouldAnalyseTargetDirectory (line 69) | #[Test]
method itShouldIgnoreExcludeFile (line 96) | #[Test]
method itShouldIgnoreExcludeFileFromDirectory (line 114) | #[Test]
method itShouldIgnoreExcludeDirectory (line 140) | #[Test]
method itShouldAnalyseTargetFileAndOutputXml (line 158) | #[Test]
method itShouldShowUsageWithNoArgs (line 178) | #[Test]
method itShouldShowVersion (line 188) | #[Test]
method itShouldShowAuthor (line 198) | private function itShouldShowAuthor()
FILE: tests/PhpAssumptions/DetectorTest.php
class DetectorTest (line 12) | class DetectorTest extends TestCase
method setUp (line 26) | public function setUp(): void
method itShouldDetectNotNull (line 32) | #[Test]
method itShouldDetectEqualsNotFalse (line 42) | #[Test]
method itShouldDetectEqualsTrue (line 61) | #[Test]
method itShouldDetectEqualsScalar (line 83) | #[Test]
method itShouldDetectWhileAssumptions (line 93) | #[Test]
method itShouldDetectForAssumptions (line 100) | #[Test]
FILE: tests/PhpAssumptions/ExampleTest.php
class ExampleTest (line 14) | class ExampleTest extends TestCase
method setUp (line 23) | public function setUp(): void
method itShouldProperlyDetectAssumptions (line 30) | #[Test]
method itShouldProperlyDetectBoolExpressions (line 66) | #[Test]
FILE: tests/PhpAssumptions/Output/PrettyOutputTest.php
class PrettyOutputTest (line 12) | class PrettyOutputTest extends TestCase
method setUp (line 31) | public function setUp(): void
method itShouldOutputWhatIsWritten (line 38) | #[Test]
FILE: tests/PhpAssumptions/Output/XmlOutputTest.php
class XmlOutputTest (line 13) | class XmlOutputTest extends TestCase
method setUp (line 37) | public function setUp(): void
method itShouldGenerateValidXml (line 45) | #[Test]
FILE: tests/PhpAssumptions/Parser/NodeVisitorTest.php
class NodeVisitorTest (line 14) | class NodeVisitorTest extends TestCase
method setUp (line 38) | public function setUp(): void
method itShouldCallScanAndWriteOnSuccess (line 49) | #[Test]
method itShouldCallScanAndNotWriteOnFailure (line 63) | #[Test]
FILE: tests/bootstrap.php
function fixture (line 6) | function fixture($filename)
FILE: tests/fixtures/Example.php
class Example (line 5) | class Example
method run (line 7) | public function run($bla)
FILE: tests/fixtures/MyClass.php
class MyClass (line 5) | class MyClass
method run (line 7) | public function run($dog, $cat)
FILE: tests/fixtures/MyOtherClass.php
class MyOtherClass (line 5) | class MyOtherClass
method run (line 7) | public function run($cat)
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (46K chars).
[
{
"path": ".gitignore",
"chars": 73,
"preview": ".idea/\nvendor/\nbuild/\ncomposer.lock\n.phpunit.result.cache\n.phpunit.cache\n"
},
{
"path": ".scrutinizer.yml",
"chars": 150,
"preview": "tools:\n external_code_coverage: true\n php_code_sniffer:\n config:\n standard: PSR2\nchecks:\n php:\n code_ratin"
},
{
"path": ".travis.yml",
"chars": 845,
"preview": "language: php\n\ninstall:\n - composer selfupdate\n - composer install --prefer-source\n\njobs:\n include:\n - stage: Unit"
},
{
"path": "CHANGELOG.md",
"chars": 701,
"preview": "# CHANGELOG\nList of changes since the very first version :)\n\n## 0.9.1 - 2025-03-22\n- Support nikic/php-parser:5.4\n- Upda"
},
{
"path": "LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Rick Kuipers\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 1753,
"preview": "# PHP Assumptions\n[](ht"
},
{
"path": "bin/phpa",
"chars": 381,
"preview": "#!/usr/bin/env php\n<?php\n\n$autoloaders = [__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'];\n\nfor"
},
{
"path": "composer.json",
"chars": 598,
"preview": "{\n \"name\": \"rskuipers/php-assumptions\",\n \"description\": \"Static code analysis tool to detect weak assumptions\",\n "
},
{
"path": "phpunit.xml.dist",
"chars": 451,
"preview": "<?xml version=\"1.0\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http"
},
{
"path": "src/PhpAssumptions/Analyser.php",
"chars": 2116,
"preview": "<?php\n\nnamespace PhpAssumptions;\n\nuse PhpAssumptions\\Output\\Result;\nuse PhpParser\\Node;\nuse PhpParser\\NodeTraverserInter"
},
{
"path": "src/PhpAssumptions/Cli.php",
"chars": 4463,
"preview": "<?php\n\nnamespace PhpAssumptions;\n\nuse League\\CLImate\\CLImate;\nuse PhpAssumptions\\Output\\PrettyOutput;\nuse PhpAssumptions"
},
{
"path": "src/PhpAssumptions/Detector.php",
"chars": 2866,
"preview": "<?php\n\nnamespace PhpAssumptions;\n\nuse PhpParser\\Node;\nuse PhpParser\\Node\\Expr;\nuse PhpParser\\Node\\Stmt;\n\nclass Detector\n"
},
{
"path": "src/PhpAssumptions/Output/OutputInterface.php",
"chars": 160,
"preview": "<?php\n\nnamespace PhpAssumptions\\Output;\n\ninterface OutputInterface\n{\n /**\n * @param Result $result\n */\n pu"
},
{
"path": "src/PhpAssumptions/Output/PrettyOutput.php",
"chars": 816,
"preview": "<?php\n\nnamespace PhpAssumptions\\Output;\n\nuse League\\CLImate\\CLImate;\n\nclass PrettyOutput implements OutputInterface\n{\n "
},
{
"path": "src/PhpAssumptions/Output/Result.php",
"chars": 1264,
"preview": "<?php\n\nnamespace PhpAssumptions\\Output;\n\nclass Result\n{\n /**\n * @var array\n */\n private $assumptions = [];"
},
{
"path": "src/PhpAssumptions/Output/XmlOutput.php",
"chars": 2505,
"preview": "<?php\n\nnamespace PhpAssumptions\\Output;\n\nuse League\\CLImate\\CLImate;\nuse PhpAssumptions\\Cli;\n\nclass XmlOutput implements"
},
{
"path": "src/PhpAssumptions/Parser/NodeVisitor.php",
"chars": 954,
"preview": "<?php\n\nnamespace PhpAssumptions\\Parser;\n\nuse PhpAssumptions\\Analyser;\nuse PhpAssumptions\\Detector;\nuse PhpParser\\Node;\nu"
},
{
"path": "tests/PhpAssumptions/AnalyserTest.php",
"chars": 1929,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions;\n\nuse PhpAssumptions\\Analyser;\nuse PhpAssumptions\\Output\\OutputInterface;\nuse PhpP"
},
{
"path": "tests/PhpAssumptions/CliTest.php",
"chars": 7026,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions;\n\nuse League\\CLImate\\Argument\\Manager;\nuse League\\CLImate\\CLImate;\nuse PhpAssumpti"
},
{
"path": "tests/PhpAssumptions/DetectorTest.php",
"chars": 3171,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions;\n\nuse PhpAssumptions\\Detector;\nuse PhpParser\\Parser;\nuse PhpParser\\ParserFactory;\n"
},
{
"path": "tests/PhpAssumptions/ExampleTest.php",
"chars": 1975,
"preview": "<?php\n\nnamespace tests;\n\nuse PhpAssumptions\\Analyser;\nuse PhpAssumptions\\Detector;\nuse PhpAssumptions\\Parser\\NodeVisitor"
},
{
"path": "tests/PhpAssumptions/Output/PrettyOutputTest.php",
"chars": 1615,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions\\Output;\n\nuse League\\CLImate\\CLImate;\nuse PhpAssumptions\\Output\\PrettyOutput;\nuse P"
},
{
"path": "tests/PhpAssumptions/Output/XmlOutputTest.php",
"chars": 2387,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions\\Output;\n\nuse League\\CLImate\\CLImate;\nuse PhpAssumptions\\Cli;\nuse PhpAssumptions\\Ou"
},
{
"path": "tests/PhpAssumptions/Parser/NodeVisitorTest.php",
"chars": 1960,
"preview": "<?php\n\nnamespace tests\\PhpAssumptions\\Parser;\n\nuse PhpAssumptions\\Analyser;\nuse PhpAssumptions\\Detector;\nuse PhpAssumpti"
},
{
"path": "tests/bootstrap.php",
"chars": 222,
"preview": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\ndefine('FIXTURES_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'fi"
},
{
"path": "tests/fixtures/Example.php",
"chars": 813,
"preview": "<?php\n\nnamespace Test;\n\nclass Example\n{\n public function run($bla)\n {\n $test = $bla && $bla === 'test' || $"
},
{
"path": "tests/fixtures/MyClass.php",
"chars": 249,
"preview": "<?php\n\nnamespace tests\\fixtures;\n\nclass MyClass\n{\n public function run($dog, $cat)\n {\n if ($dog !== null) {"
},
{
"path": "tests/fixtures/MyOtherClass.php",
"chars": 164,
"preview": "<?php\n\nnamespace tests\\fixtures;\n\nclass MyOtherClass\n{\n public function run($cat)\n {\n if ($cat !== null) {\n"
}
]
About this extraction
This page contains the full source code of the rskuipers/php-assumptions GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (41.7 KB), approximately 11.6k tokens, and a symbol index with 80 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.