Skip to content

Commit

Permalink
Merge pull request #218 from veewee/phpparser
Browse files Browse the repository at this point in the history
Php_parser task
  • Loading branch information
veewee authored Nov 8, 2016
2 parents b689bc1 + 276a981 commit 5150603
Show file tree
Hide file tree
Showing 58 changed files with 2,784 additions and 25 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ To make GrumPHP even more awesome, it will suggest installing some extra package
- codeception/codeception : ~2.1
- sensiolabs/security-checker : ~3.0
- phpmd/phpmd : ~2.4
- nikic/php-parser: ~2.1

GrumPHP will never push you into using a specific task. You can choose the tasks that fit your needs, and activate or
deactivate any task in no time!
Expand Down Expand Up @@ -111,6 +112,7 @@ parameters:
phpcsfixer2: ~
phplint: ~
phpmd: ~
phpparser: ~
phpspec: ~
phpunit: ~
robo: ~
Expand Down
25 changes: 13 additions & 12 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@
"gitonomy/gitlib": "~1.0",
"monolog/monolog": "~1.17",
"seld/jsonlint": "~1.1",
"symfony/config": "~2.4|~3.0",
"symfony/console": "~2.6|~3.0",
"symfony/dependency-injection": "~2.4|~3.0",
"symfony/event-dispatcher": "~2.5|~3.0",
"symfony/filesystem": "~2.4|~3.0",
"symfony/finder": "~2.4|~3.0",
"symfony/options-resolver": "~2.6|~3.0",
"symfony/process": "~2.4|~3.0",
"symfony/proxy-manager-bridge": "~2.6|~3.0",
"symfony/yaml": "~2.4|~3.0"
"symfony/config": "~2.7|~3.0",
"symfony/console": "~2.7|~3.0",
"symfony/dependency-injection": "~2.7|~3.0",
"symfony/event-dispatcher": "~2.7|~3.0",
"symfony/filesystem": "~2.7|~3.0",
"symfony/finder": "~2.7|~3.0",
"symfony/options-resolver": "~2.7|~3.0",
"symfony/process": "~2.7|~3.0",
"symfony/proxy-manager-bridge": "~2.7|~3.0",
"symfony/yaml": "~2.7|~3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~1|~2",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"nikic/php-parser": "~2.1",
"phpspec/phpspec": "~2.5",
"phpspec/prophecy": "~1.6",
"phpunit/phpunit": "^4.8",
"sensiolabs/security-checker": "^3.0",
"squizlabs/php_codesniffer": "~2.3",
"sstalle/php7cc": "^1.1"
"squizlabs/php_codesniffer": "~2.3"
},
"suggest": {
"atoum/atoum": "Lets GrumPHP run your unit tests.",
Expand All @@ -41,6 +41,7 @@
"friendsofphp/php-cs-fixer": "Lets GrumPHP automatically fix your codestyle.",
"jakub-onderka/php-parallel-lint": "Lets GrumPHP quickly lint your entire code base.",
"malukenho/kawaii-gherkin": "Lets GrumPHP lint your Gherkin files.",
"nikic/php-parser": "Lets GrumPHP run static analyses through your PHP files.",
"phing/phing": "Lets GrumPHP run your automated PHP tasks.",
"phpmd/phpmd": "Lets GrumPHP sort out the mess in your code",
"phpspec/phpspec": "Lets GrumPHP spec your code.",
Expand Down
6 changes: 4 additions & 2 deletions doc/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ parameters:
phpcs: ~
phpcsfixer: ~
phpcsfixer2: ~
phpmd: ~
phplint: ~
phpmd: ~
phpparser: ~
phpspec: ~
phpunit: ~
robo: ~
Expand Down Expand Up @@ -68,8 +69,9 @@ Every task has it's own default configuration. It is possible to overwrite the p
- [Phpcs](tasks/phpcs.md)
- [PHP-CS-Fixer](tasks/php_cs_fixer.md)
- [PHP-CS-Fixer 2](tasks/php_cs_fixer2.md)
- [PhpMd](tasks/phpmd.md)
- [PHPLint](tasks/phplint.md)
- [PhpMd](tasks/phpmd.md)
- [PhpParser](tasks/phpparser.md)
- [Phpspec](tasks/phpspec.md)
- [Phpunit](tasks/phpunit.md)
- [Robo](tasks/robo.md)
Expand Down
6 changes: 6 additions & 0 deletions doc/tasks/php7cc.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ Minimum issue level. There are 3 issue levels: "info", "warning" and "error". "i
*Default: [php]*
This is a list of extensions to be sniffed.
## Known issues
- Since this task is using an old version of phpparser, it currently cannot be used in combination with the `phpparser` task.
[Click here for more information](https://github.com/sstalle/php7cc/issues/79)
303 changes: 303 additions & 0 deletions doc/tasks/phpparser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
# Phpparser

The Phpparser task will run static code analyses on your PHP code.
You can specify which code visitors should run on your code or write your own code visitor.
It lives under the `php_parser` namespace and has following configurable parameters:

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
ignore_patterns: []
kind: php7
triggered_by: [php]
visitors: {}
```
**triggered_by**
*Default: [php]*
This option will specify which file extensions will trigger the php blacklist task.
By default php blacklist will be triggered by altering a php file.
You can overwrite this option to whatever filetype you want to validate!
**ignore_patterns**
*Default: []*
This is a list of patterns that will be ignored by the PHP Parser.
With this option you can skip files like tests. Leave this option blank to run analysis for every php file.
**kind**
*Default: php7*
Can be one of: php5, php7.
This option determines which Lexer the PHP_Parser uses to tokenize the PHP code.
By default the PREFER_PHP7 is loaded.
**visitors**
*Default: {}*
Use this parameter to specify what code you want to scan. This is made possible by node visitors following PHP_Parser syntax.
Without any visitors specified, PHP Parser will only check code syntax (similar to php lint).
In the next chapter, you can find a list of built-in visitors.
It's also possible to write your own visitor!
## Built-in visitors
- [declare_strict_types](#declare_strict_types)
- [forbidden_class_method_calls](#forbidden_class_method_calls)
- [forbidden_function_calls](#forbidden_function_calls)
- [forbidden_static_method_calls](#forbidden_static_method_calls)
- [nameresolver](#nameresolver)
- [never_use_else](#never_use_else)
- [no_exit_statements](#no_exit_statements)
### declare_strict_types
This visitor can be used to enforce `declare(strict_types=1)` in every PHP file.

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
declare_strict_types: ~
```

This visitore is not configurable!


### forbidden_class_method_calls

This visitor can be used to look for forbidden class method calls.

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
forbidden_class_method_calls:
blacklist:
- '$dumper->dump'
```

**blacklist**

*Default: []*

This is a list of blacklisted class method calls. The syntax is `$variableName->methodName`.
When one of the functions inside this list is being called by your code,
the parser will markt this method as an error.


### forbidden_function_calls

This visitor can be used to look for forbidden function calls.

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
forbidden_function_calls:
blacklist:
- 'var_dump'
```

**blacklist**

*Default: []*

This is a list of blacklisted function calls.
When one of the functions inside this list is being called by your code,
the parser will markt this method as an error.


### forbidden_static_method_calls

This visitor can be used to look for forbidden static method calls.

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
forbidden_static_method_calls:
blacklist:
- 'Dumper::dump'
```

**blacklist**

*Default: []*

This is a list of blacklisted static method calls. The syntax is `Fully\Qualified\ClassName::staticMethodName`.
When one of the functions inside this list is being called by your code,
the parser will markt this method as an error.


### nameresolver

This visitor is an alias for the built-in PhpParser NameResolver visitor.
It looks for class aliases in your code and adds the alias as an attribute to the class nodes.

*Note:* This visitor is enabled by default since it is used by other visitors.
You don't have to register it in the task configuration.

This visitor is not configurable!


### never_use_else

This visitor will search for the `else` and `elseif` keywords in your code.
An error will be added if one of those statements is found.
More information about Object Calisthenics can be found
[here](http://www.slideshare.net/rdohms/your-code-sucks-lets-fix-it-15471808)
and
[here](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php).

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
never_use_else: ~
```

This visitor is not configurable!


### no_exit_statements

This visitor will search for exit statements like `die()` or `exit` in your code.
An error will be added if an exit statement is found.

```yaml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
no_exit_statements: ~
```

This visitor is not configurable!


## Creating your own visitor

Creating your own visitor is easy!
Just create a class that implements the `PhpParser\NodeVisitor` interface:

```php
// PhpParser\NodeVisitor
interface NodeVisitor
{
public function beforeTraverse(array $nodes);
public function enterNode(Node $node);
public function leaveNode(Node $node);
public function afterTraverse(array $nodes);
}
```

Once you've written your visitor, you'll have to register it to the service container:

```yaml
services:
grumphp.parser.php.visitor.your_visitor:
class: 'Your\Visitor\Class'
arguments: []
tags:
- {name: 'php_parser.visitor', alias: 'your_visitor'}
```

Since we use the service container, you are able to inject the dependencies you need with the `arguments` attribute.
The `php_parser.visitor` tag will make your class available in GrumPHP.
Tha alias `your_visitor` can now be set as a visitor in the phpparser task:

```yml
# grumphp.yml
parameters:
tasks:
phpparser:
visitors:
your_visitor: ~
```

### Stateless visitors

An important note on the visitors is that they are completely stateless!
If you've already written a PhpParser Visitor before, you know that advanced visitors will contain a specific state about the class.
When scanning the next file, the state needs to be reset.

In our implementation, we've chosen to always create a new visitor for every file.
This way, you don't have to think about clearing the state of a visitor on each run.
This will result in easy and understandable visitors!


### Optional interfaces and classes

We also added some optional interfaces and to make it easier to interct with the GrumPHP context:


**ConfigurableVisitorInterface**

The `ConfigurableVisitorInterface` allows you to make the visitor configurable in the `grumphp.yml` file.
To make sure the visitor works as you please, It is recommended to use the `OptionsResolver` to validate the configured options.


```php
// GrumPHP\Parser\Php\Visitor\ConfigurableVisitorInterface;
interface ConfigurableVisitorInterface extends NodeVisitor
{
public function configure(array $options);
}
```


**ContextAwareVisitorInterface**

The `ContextAwareVisitorInterface` will make your task aware of the context the visitor is running in.
The `ParserContext` object will have access to the file information and the errors collection.
This last one can be used to add a new error in your visitor.

```php
// GrumPHP\Parser\Php\Visitor\ContextAwareVisitorInterface
interface ContextAwareVisitorInterface extends NodeVisitor
{
public function setContext(ParserContext $context);
}
```

**AbstractVisitor**

In the built-in visitors, we use the `AbstractVisitor` that extends the `PhpParser\NodeVisitorAbstract`.
This means that you only have to implement the methods from the `NodeVisitor` that you need.
It also implements the `ContextAwareVisitorInterface` and provides an easy method for logging errors in your custom visitor.
The bleuprint of this abstract visitor looks like this:


```php
// GrumPHP\Parser\Php\Visitor\AbstractVisitor;
class AbstractVisitor extends NodeVisitorAbstract implements ContextAwareVisitorInterface
{
protected $context;
public function setContext(ParserContext $context);
protected function addError($message, $line = -1, $type = ParseError::TYPE_ERROR);
}
```
Loading

0 comments on commit 5150603

Please sign in to comment.