deeem/hexlet-psr-linter

View on GitHub
sniffs/SideEffect.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

namespace PsrLinter\Rules;

class SideEffect extends CheckersTemplate implements RulesInterface
{
    const DECLARATION = 'declaration';
    const SIDE_EFFECT = 'side effect';
    private $containsMixed = false;
    private $containsDeclarations = false;
    private $containsSideEffects = false;

    /**
     * Define what node actually do:
     * * declare new symbols (classes, functions, constants, etc.)
     * * or it execute logic with side effects
     *
     * @param $node
     *
     * @return string 'declaration' or 'side effect'
     */
    private function defineNode($node)
    {
        $definitions = [
            function ($node) {
                // for example: include 'file.php';
                if ($node instanceof \PhpParser\Node\Expr\Include_) {
                    return self::SIDE_EFFECT;
                }
            },
            function ($node) {
                // for example: ini_set('error_reporting', E_ALL);
                if ($node instanceof \PhpParser\Node\Expr\FuncCall) {
                    $parts = $node->name->parts[0] ?? '';
                    if ($parts == 'ini_set') {
                        return self::SIDE_EFFECT;
                    }
                }
            },
            function ($node) {
                // for example: echo "<html>\n";
                if ($node instanceof \PhpParser\Node\Stmt\Echo_) {
                    return self::SIDE_EFFECT;
                }
            },
            function ($node) {
                // for example: class Foo{}
                if ($node instanceof \PhpParser\Node\Stmt\Class_) {
                    return self::DECLARATION;
                }
            },
            function ($node) {
                // for example: function foo(){}
                if ($node instanceof \PhpParser\Node\Stmt\Function_) {
                    return self::DECLARATION;
                }
            },
            function ($node) {
                // for example: const FOO = '';
                if ($node instanceof \PhpParser\Node\Const_) {
                    return self::DECLARATION;
                }
            }
        ];

        foreach ($definitions as $define) {
            $definition = $define($node);
            if ($definition == self::DECLARATION || $definition == self::SIDE_EFFECT) {
                return $definition;
            }
        }
    }

    public function check(\PhpParser\Node $node)
    {
        if ($this->containsMixed) {
            return;
        }

        $definition = $this->defineNode($node);

        if ($definition == self::DECLARATION) {
            $this->containsDeclarations = true;
        } elseif ($definition == self::SIDE_EFFECT) {
            $this->containsSideEffects = true;
        }

        if ($this->containsSideEffects && $this->containsDeclarations) {
            $this->containsMixed = true;

            $reason ='A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no ' .
                     'other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both.';
            $this->addError($node, 'warning', $reason, $definition);
        }
    }
}