public function testGetClassyElements()
    {
        $source = <<<'PHP'
<?php
class Foo
{
    public $prop0;
    protected $prop1;
    private $prop2 = 1;
    var $prop3 = array(1,2,3);

    public function bar4()
    {
        $a = 5;

        return " ({$a})";
    }
    public function bar5($data)
    {
    }
}
PHP;
        $tokens = Tokens::fromCode($source);
        $tokensAnalyzer = new TokensAnalyzer($tokens);
        $elements = array_values($tokensAnalyzer->getClassyElements());
        $this->assertCount(6, $elements);
        $this->assertSame('property', $elements[0]['type']);
        $this->assertSame('property', $elements[1]['type']);
        $this->assertSame('property', $elements[2]['type']);
        $this->assertSame('property', $elements[3]['type']);
        $this->assertSame('method', $elements[4]['type']);
        $this->assertSame('method', $elements[5]['type']);
    }
 /**
  * @inheritdoc
  */
 public function check(\SplFileInfo $file, Tokens $tokens)
 {
     $analyzer = new TokensAnalyzer($tokens);
     $constructorIndex = null;
     $constructorArgs = [];
     $properties = [];
     // Find the class
     $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]);
     if ($classIndex === null) {
         return;
     }
     $classAttributes = TokensHelper::getClassAttributes($tokens, $classIndex);
     // Event must be final or abstract
     if (!$classAttributes['abstract'] && !$classAttributes['final']) {
         $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_be_final_or_abstract', ['class' => $classAttributes['name']]));
     }
     $elements = $analyzer->getClassyElements();
     foreach ($elements as $index => $element) {
         /** @var Token $token */
         if ($element['type'] === 'method') {
             $methodAttributes = TokensHelper::getMethodAttributes($tokens, $index, $analyzer);
             if ($methodInfo = $this->checkMethod($tokens, $index, $methodAttributes)) {
                 $constructorIndex = $index;
                 $constructorArgs = $methodInfo['arguments'];
             }
         } elseif ($element['type'] === 'property' && ($propertyInfo = $this->checkProperty($tokens, $index))) {
             $properties[$index] = $propertyInfo;
         }
     }
     // A event without data is not a error
     if ($constructorIndex === null && count($properties) === 0) {
         return;
     }
     // Event with properties must have a constructor
     if ($constructorIndex === null) {
         $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_have_constructor', ['class' => $classAttributes['name']]));
     } else {
         $expectedProperties = array_map(function ($info) {
             return $info['token']->getContent();
         }, $constructorArgs);
         $unknownConstructorArgs = array_diff($expectedProperties, $properties);
         if (count($unknownConstructorArgs) > 0) {
             $tokens->reportAt($constructorIndex, new Message(E_ERROR, 'check_class_constructor_has_arguments_without_related_property', ['arguments' => implode(', ', $unknownConstructorArgs)]));
         }
         $unassignedProperties = array_diff($properties, $expectedProperties);
         if (count($unassignedProperties) > 0) {
             foreach ($unassignedProperties as $index => $property) {
                 $tokens->reportAt($index, new Message(E_ERROR, 'check_class_property_must_be_know_as_constructor_argument', ['property' => $property]));
             }
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function fix(\SplFileInfo $file, Tokens $tokens)
 {
     $tokensAnalyzer = new TokensAnalyzer($tokens);
     $elements = $tokensAnalyzer->getClassyElements();
     foreach (array_reverse($elements, true) as $index => $element) {
         if ('method' === $element['type']) {
             $this->overrideAttribs($tokens, $index, $this->grabAttribsBeforeMethodToken($tokens, $index));
             // force whitespace between function keyword and function name to be single space char
             $afterToken = $tokens[++$index];
             if ($afterToken->isWhitespace()) {
                 $afterToken->setContent(' ');
             }
         } elseif ('property' === $element['type']) {
             $prevIndex = $tokens->getPrevTokenOfKind($index, array(';', ',', '{'));
             if (!$prevIndex || !$tokens[$prevIndex]->equals(',')) {
                 $this->overrideAttribs($tokens, $index, $this->grabAttribsBeforePropertyToken($tokens, $index));
             }
         }
     }
 }