Skip to content

Commit

Permalink
Merge pull request #24 from voda/phpstan
Browse files Browse the repository at this point in the history
add phpstan with fixes
  • Loading branch information
voda committed Apr 21, 2019
2 parents 0a451e0 + e19d78c commit 2965a81
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 100 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Expand Up @@ -2,5 +2,8 @@ language: php
php:
- 7.3
- 7.2
before_script:
install:
- composer install --no-interaction --prefer-dist
script:
- ./vendor/bin/phpunit
- ./vendor/bin/phpstan analyse
7 changes: 5 additions & 2 deletions composer.json
Expand Up @@ -10,7 +10,8 @@
"require": {
"php": ">=7.2.0",
"nikic/php-parser": "~4.2.0",
"latte/latte": "~2.5.0"
"latte/latte": "~2.5.0",
"nette/utils": "^3.0"
},
"autoload": {
"psr-4": {"Vodacek\\GettextExtractor\\" : "src"}
Expand All @@ -25,6 +26,8 @@
"gettext-extractor.php"
],
"require-dev": {
"phpunit/phpunit": "^8"
"phpunit/phpunit": "^8",
"phpstan/phpstan-shim": "^0.11.5",
"phpstan/phpstan-phpunit": "^0.11.0"
}
}
6 changes: 3 additions & 3 deletions gettext-extractor.php
Expand Up @@ -75,9 +75,9 @@
$keywords[] = array(
'filter' => $filter,
'function' => $function,
'singular' => isset($params[0]) ? $params[0] : null,
'plural' => isset($params[1]) ? $params[1] : null,
'context' => isset($params[2]) ? $params[2] : null
'singular' => isset($params[0]) ? (int) $params[0] : null,
'plural' => isset($params[1]) ? (int) $params[1] : null,
'context' => isset($params[2]) ? (int) $params[2] : null
);
}
}
Expand Down
7 changes: 7 additions & 0 deletions makefile
@@ -0,0 +1,7 @@
check: test phpstan

test:
./vendor/bin/phpunit

phpstan:
./vendor/bin/phpstan analyse
13 changes: 13 additions & 0 deletions phpstan.neon.dist
@@ -0,0 +1,13 @@
parameters:
level: 7
paths:
- src
- tests
# - gettext-extractor.php
excludes_analyse:
- tests/integration/data
- tests/unit/data

includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
5 changes: 3 additions & 2 deletions src/Extractor.php
Expand Up @@ -8,6 +8,7 @@

namespace Vodacek\GettextExtractor;

use Nette\Utils\FileSystem;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RuntimeException;
Expand Down Expand Up @@ -83,7 +84,7 @@ protected function throwException(string $message): void {
/**
* Scans given files or directories and extracts gettext keys from the content
*
* @param string|array $resource
* @param string|string[] $resource
* @return self
*/
public function scan($resource): self {
Expand Down Expand Up @@ -237,7 +238,7 @@ public function setMeta(string $key, string $value): self {
* @return self
*/
public function save(string $outputFile, array $data = null): self {
file_put_contents($outputFile, $this->formatData($data ?: $this->data));
FileSystem::write($outputFile, $this->formatData($data ?: $this->data));
return $this;
}

Expand Down
10 changes: 5 additions & 5 deletions src/Filters/AFilter.php
Expand Up @@ -18,10 +18,10 @@ abstract class AFilter {
/**
* Includes a function to parse gettext phrases from
*
* @param $functionName string
* @param $singular int
* @param $plural int|null
* @param $context int|null
* @param string $functionName
* @param int $singular
* @param int|null $plural
* @param int|null $context
* @return self
*/
public function addFunction(string $functionName, int $singular = 1, int $plural = null, int $context = null): self {
Expand Down Expand Up @@ -50,7 +50,7 @@ public function addFunction(string $functionName, int $singular = 1, int $plural
/**
* Excludes a function from the function list
*
* @param $functionName
* @param string $functionName
* @return self
*/
public function removeFunction(string $functionName): self {
Expand Down
25 changes: 17 additions & 8 deletions src/Filters/LatteFilter.php
Expand Up @@ -8,6 +8,10 @@

namespace Vodacek\GettextExtractor\Filters;

use Nette\Utils\FileSystem;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Expression;
use Vodacek\GettextExtractor\Extractor;
use PhpParser;
use Latte;
Expand All @@ -29,7 +33,7 @@ public function extract(string $file): array {
$data = array();

$latteParser = new Latte\Parser();
$tokens = $latteParser->parse(file_get_contents($file));
$tokens = $latteParser->parse(FileSystem::read($file));

$functions = array_keys($this->functions);
usort($functions, static function(string $a, string $b) {
Expand All @@ -49,25 +53,30 @@ public function extract(string $file): array {
$value = $this->trimMacroValue($name, $token->value);
$stmts = $phpParser->parse("<?php\nf($value);");

foreach ($this->functions[$name] as $definition) {
$message = $this->processFunction($definition, $stmts[0]->expr);
if ($message) {
$message[Extractor::LINE] = $token->line;
$data[] = $message;
if ($stmts === null) {
continue;
}
if ($stmts[0] instanceof Expression && $stmts[0]->expr instanceof FuncCall) {
foreach ($this->functions[$name] as $definition) {
$message = $this->processFunction($definition, $stmts[0]->expr);
if ($message) {
$message[Extractor::LINE] = $token->line;
$data[] = $message;
}
}
}
}
return $data;
}

private function processFunction(array $definition, PhpParser\Node\Expr\FuncCall $node): array {
private function processFunction(array $definition, FuncCall $node): array {
$message = [];
foreach ($definition as $type => $position) {
if (!isset($node->args[$position - 1])) {
return [];
}
$arg = $node->args[$position - 1]->value;
if ($arg instanceof PhpParser\Node\Scalar\String_) {
if ($arg instanceof String_) {
$message[$type] = $arg->value;
} else {
return [];
Expand Down
53 changes: 37 additions & 16 deletions src/Filters/PHPFilter.php
Expand Up @@ -7,13 +7,23 @@

namespace Vodacek\GettextExtractor\Filters;

use Nette\Utils\FileSystem;
use PhpParser;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use Vodacek\GettextExtractor\Extractor;

class PHPFilter extends AFilter implements IFilter, PhpParser\NodeVisitor {

/** @var array */
private $data;
private $data = [];

public function __construct() {
$this->addFunction('gettext', 1);
Expand All @@ -29,47 +39,58 @@ public function __construct() {
public function extract(string $file): array {
$this->data = array();
$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::PREFER_PHP7);
$stmts = $parser->parse(file_get_contents($file));
$stmts = $parser->parse(FileSystem::read($file));
if ($stmts === null) {
return [];
}
$traverser = new PhpParser\NodeTraverser();
$traverser->addVisitor($this);
$traverser->traverse($stmts);
$data = $this->data;
$this->data = null;
$this->data = [];
return $data;
}

public function enterNode(PhpParser\Node $node) {
public function enterNode(Node $node) {
$name = null;
if (($node instanceof PhpParser\Node\Expr\MethodCall || $node instanceof PhpParser\Node\Expr\StaticCall) && $node->name instanceof PhpParser\Node\Identifier && is_string($node->name->name)) {
$args = [];
if (($node instanceof MethodCall || $node instanceof StaticCall) && $node->name instanceof Identifier && is_string($node->name->name)) {
$name = $node->name->name;
} elseif ($node instanceof PhpParser\Node\Expr\FuncCall && $node->name instanceof PhpParser\Node\Name) {
$args = $node->args;
} elseif ($node instanceof FuncCall && $node->name instanceof Name) {
$parts = $node->name->parts;
$name = array_pop($parts);
$args = $node->args;
} else {
return;
return null;
}
if (!isset($this->functions[$name])) {
return;
return null;
}
foreach ($this->functions[$name] as $definition) {
$this->processFunction($definition, $node);
$this->processFunction($definition, $node, $args);
}
}

private function processFunction(array $definition, PhpParser\Node $node) {
/**
* @param array $definition
* @param Node $node
* @param Arg[] $args
*/
private function processFunction(array $definition, Node $node, array $args) {
$message = array(
Extractor::LINE => $node->getLine()
);
foreach ($definition as $type => $position) {
if (!isset($node->args[$position - 1])) {
if (!isset($args[$position - 1])) {
return;
}
$arg = $node->args[$position - 1]->value;
if ($arg instanceof PhpParser\Node\Scalar\String_) {
$arg = $args[$position - 1]->value;
if ($arg instanceof String_) {
$message[$type] = $arg->value;
} elseif ($arg instanceof PhpParser\Node\Expr\Array_) {
} elseif ($arg instanceof Array_) {
foreach ($arg->items as $item) {
if ($item->value instanceof PhpParser\Node\Scalar\String_) {
if ($item->value instanceof String_) {
$message[$type][] = $item->value->value;
}
}
Expand Down Expand Up @@ -99,6 +120,6 @@ public function afterTraverse(array $nodes) {
public function beforeTraverse(array $nodes) {
}

public function leaveNode(PhpParser\Node $node) {
public function leaveNode(Node $node) {
}
}
24 changes: 16 additions & 8 deletions src/NetteExtractor.php
Expand Up @@ -8,12 +8,12 @@

namespace Vodacek\GettextExtractor;

use Vodacek\GettextExtractor\Filters\LatteFilter;
use Vodacek\GettextExtractor\Filters\PHPFilter;

class NetteExtractor extends Extractor {

/**
* @param string|bool $logToFile
*/
public function __construct($logToFile = false) {
public function __construct(string $logToFile = 'php://stderr') {
parent::__construct($logToFile);

// Clean up...
Expand All @@ -28,11 +28,15 @@ public function __construct($logToFile = false) {

$this->addFilter('Latte', new Filters\LatteFilter());

$this->getFilter('PHP')
->addFunction('translate');
$phpFilter = $this->getFilter('PHP');
assert($phpFilter instanceof PHPFilter);

$phpFilter->addFunction('translate');

$this->getFilter('Latte')
->addFunction('!_')
$latteFilter = $this->getFilter('Latte');
assert($latteFilter instanceof LatteFilter);

$latteFilter->addFunction('!_')
->addFunction('_');
}

Expand All @@ -43,6 +47,8 @@ public function __construct($logToFile = false) {
*/
public function setupForms(): self {
$php = $this->getFilter('PHP');
assert($php instanceof PHPFilter);

$php->addFunction('setText')
->addFunction('setEmptyValue')
->addFunction('setValue')
Expand Down Expand Up @@ -80,6 +86,8 @@ public function setupForms(): self {
*/
public function setupDataGrid(): self {
$php = $this->getFilter('PHP');
assert($php instanceof PHPFilter);

$php->addFunction('addColumn', 2)
->addFunction('addNumericColumn', 2)
->addFunction('addDateColumn', 2)
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/NetteFormsTest.php
Expand Up @@ -20,7 +20,11 @@ public function testForm(): void {
$this->object->setupForms();
$this->object->scan('tests/integration/data/form.php');
$temp = tempnam(sys_get_temp_dir(), __CLASS__);
if ($temp === false) {
throw new \RuntimeException('Failed to create temporary file.');
}
$this->object->save($temp);
$this->assertFileEquals(__DIR__.'/data/form.pot', $temp);
unlink($temp);
}
}
5 changes: 3 additions & 2 deletions tests/unit/GettextExtractor/Filters/AFilterTest.php
@@ -1,15 +1,16 @@
<?php
declare(strict_types=1);

namespace Vodacek\GettextExtractor\Tests\Integration;
namespace Vodacek\GettextExtractor\Tests\Unit\GettextExtractor\Filters;

use InvalidArgumentException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Vodacek\GettextExtractor as GE;

class AFilterTest extends TestCase {

/** @var GE\Filters\AFilter */
/** @var MockObject&GE\Filters\AFilter */
protected $object;

protected function setUp(): void {
Expand Down

0 comments on commit 2965a81

Please sign in to comment.