/** * Perform the analysis for special properties and write them to the files. * * Use addSource() before calling this. * * @return int Number of source files changed (does not necessarily mean they * have been actually modified in case dryRun if true) */ public function applyMagic() { $sourcesChanged = 0; # start processing $traverser = new NodeTraverser(); $classVisitor = new ClassVisitor(); $traverser->addVisitor($classVisitor); # Store which class is in which file and whose parent it has $classes = []; foreach ($this->files as $file) { $this->logger->info('Parsing ' . $file->getRealPath()); $code = file($file); $parser = new Parser(new Lexer()); $tree = $parser->parse(join('', $code)); $traverser->traverse($tree); $classesInFile = $classVisitor->getClasses(); foreach ($classesInFile as $class) { # A class without an parent cannot be any CakePHP managed class if (!isset($class->extends)) { continue; } if (isset($classes[$class->name])) { $this->logger->error(sprintf('Ignoring class definition of %s found in file %s:%d, first' . ' found in %s:%d', $class->name, $file, $class->getAttribute('startLine'), $classes[$class->name]['file'], $classes[$class->name]['class']->getAttribute('startLine'))); continue; } # Remember for next pass $classes[$class->name] = ['class' => $class, 'file' => $file->getRealPath(), 'code' => $code]; } } $traverser->removeVisitor($classVisitor); $propertyVisitor = new PropertyVisitor(); $propertyVisitor->setProperties($this->getProperties()); $traverser->addVisitor($propertyVisitor); if (empty($classes)) { $this->logger->warning('No classes found'); return $sourcesChanged; } $fnFindTopAncestor = function ($className) use($classes) { while (isset($classes[$className])) { $className = $classes[$className]['class']->extends->toString(); } return $className; }; foreach ($classes as $className => $classData) { $fileName = $classData['file']; $transformedSource = NULL; $topAncestor = $fnFindTopAncestor($className); if (!isset($this->classToPropertySymbolTransform[$topAncestor])) { $this->logger->info("Ignoring {$fileName} , not a recognized class"); continue; } $traverser->traverse([$classData['class']]); $transformedSource = ClassTransformer::apply($classData['code'], $propertyVisitor->getClasses(), $this->classToPropertySymbolTransform[$topAncestor], $this->removeUnknownProperties); if ($classData['code'] === $transformedSource) { continue; } $sourcesChanged++; if ($this->dryRun) { $this->logger->info('Dry-run, not writing changes to ' . $fileName); continue; } $this->logger->info('Writing changes to ' . $fileName); file_put_contents($fileName, $transformedSource); } return $sourcesChanged; }
public function testString2LinesEmptyLineMiddle() { $actual = ClassTransformer::splitStringIntoLines("line1\n\nline2\n"); $expected = ["line1\n", "\n", "line2\n"]; $this->assertSame($expected, $actual); }
public function testTransformerPluginNames() { $sourceIn = ['<?php' . PHP_EOL, '/**' . PHP_EOL, ' */' . PHP_EOL, 'class Foo {' . PHP_EOL, ' var $uses = [\'Bar.Baz\'];' . PHP_EOL, '}' . PHP_EOL]; $sourceOutExpected = ['<?php' . PHP_EOL, '/**' . PHP_EOL, ' * @property Baz $Baz' . PHP_EOL, ' */' . PHP_EOL, 'class Foo {' . PHP_EOL, ' var $uses = [\'Bar.Baz\'];' . PHP_EOL, '}' . PHP_EOL]; $properties = ['uses' => function ($sym) { return $sym; }]; $tree = $this->parser->parse(join('', $sourceIn)); $this->visitor->setProperties(array_keys($properties)); $this->traverser->traverse($tree); $sourceOutActual = ClassTransformer::apply($sourceIn, $this->visitor->getClasses(), $properties); $this->assertSame($sourceOutExpected, $sourceOutActual); }