diff --git a/src/TranslationSourceLocationContainer.php b/src/TranslationSourceLocationContainer.php new file mode 100644 index 0000000..6d04d4f --- /dev/null +++ b/src/TranslationSourceLocationContainer.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Extractor; + +use Translation\Extractor\Model\SourceLocation; + +/** + * This interface is recognized by the extractors. Use this on your Form classes + * or anywhere where you have dynamic translation strings. + * + * @author Tobias Nyholm + */ +interface TranslationSourceLocationContainer +{ + /** + * Return an array of source locations. + * + * @return SourceLocation[] + */ + public static function getTranslationSourceLocations(); +} diff --git a/src/Visitor/Php/SourceLocationContainerExtractor.php b/src/Visitor/Php/SourceLocationContainerExtractor.php new file mode 100644 index 0000000..2a5da2b --- /dev/null +++ b/src/Visitor/Php/SourceLocationContainerExtractor.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Extractor\Visitor\Php; + +use PhpParser\Node; +use PhpParser\NodeVisitor; +use Translation\Extractor\Model\SourceLocation; + +/** + * Extract translations from classes implementing + * Translation\Extractor\Model\SourceLocation\TranslationSourceLocationContainer. + * + * @author Tobias Nyholm + */ +class SourceLocationContainerExtractor extends BasePHPVisitor implements NodeVisitor +{ + /** + * @var string + */ + private $namespace = ''; + + /** + * @var array + */ + private $useStatements = []; + + public function beforeTraverse(array $nodes) + { + } + + public function enterNode(Node $node) + { + if ($node instanceof Node\Stmt\Namespace_) { + if (isset($node->name)) { + // Save namespace of this class for later. + $this->namespace = implode('\\', $node->name->parts); + } + $this->useStatements = []; + + return; + } + + if ($node instanceof Node\Stmt\UseUse) { + $this->useStatements[$node->alias] = implode('\\', $node->name->parts); + + return; + } + + if (!$node instanceof Node\Stmt\Class_) { + return; + } + + $isContainer = false; + foreach ($node->implements as $interface) { + $name = implode('\\', $interface->parts); + if (isset($this->useStatements[$name])) { + $name = $this->useStatements[$name]; + } + + if ('Translation\Extractor\TranslationSourceLocationContainer' === $name) { + $isContainer = true; + break; + } + } + + if (!$isContainer) { + return; + } + + $sourceLocations = call_user_func([$this->namespace.'\\'.$node->name, 'getTranslationSourceLocations']); + if (!is_array($sourceLocations)) { + throw new \RuntimeException(sprintf('%s::getTranslationSourceLocations() was expected to return an array of SourceLocations, but got %s.', $this->namespace.'\\'.$node->name, gettype($sourceLocations))); + } + + foreach ($sourceLocations as $sourceLocation) { + if (!$sourceLocation instanceof SourceLocation) { + throw new \RuntimeException(sprintf('%s::getTranslationSourceLocations() was expected to return an array of SourceLocations, but got an array which contains an item of type %s.', $this->namespace.'\\'.$node->name, gettype($sourceLocation))); + } + + $this->collection->addLocation($sourceLocation); + } + } + + public function leaveNode(Node $node) + { + } + + public function afterTraverse(array $nodes) + { + } +} diff --git a/tests/Functional/Visitor/Php/SourceLocationContainerExtractorTest.php b/tests/Functional/Visitor/Php/SourceLocationContainerExtractorTest.php new file mode 100644 index 0000000..560ab27 --- /dev/null +++ b/tests/Functional/Visitor/Php/SourceLocationContainerExtractorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Extractor\Tests\Functional\Visitor\Php; + +use Translation\Extractor\Visitor\Php\SourceLocationContainerExtractor; +use Translation\Extractor\Tests\Resources; + +class SourceLocationContainerExtractorTest extends BasePHPVisitorTest +{ + public function testExtract() + { + $collection = $this->getSourceLocations(new SourceLocationContainerExtractor(), Resources\Php\SourceLocationContainer::class); + + $this->assertCount(2, $collection); + $source = $collection->first(); + $this->assertEquals('foo', $source->getMessage()); + } +} diff --git a/tests/Resources/Php/SourceLocationContainer.php b/tests/Resources/Php/SourceLocationContainer.php new file mode 100644 index 0000000..ca696ab --- /dev/null +++ b/tests/Resources/Php/SourceLocationContainer.php @@ -0,0 +1,17 @@ +