├── .gitignore ├── tests ├── Doctrine │ └── Tests │ │ └── CodeGenerator │ │ ├── _files │ │ └── test.php │ │ ├── ProjectWriterTest.php │ │ ├── EventGeneratorTest.php │ │ ├── TestCase.php │ │ ├── Listener │ │ ├── GenerateClassesListenerTest.php │ │ ├── FluentSetterListenerTest.php │ │ ├── GetterSetterListenerTest.php │ │ ├── TimestampableListenerTest.php │ │ ├── ORM │ │ │ └── CollectionListenerTest.php │ │ └── DocListenerTest.php │ │ ├── Builder │ │ ├── MethodBuilderTest.php │ │ ├── PropertyBuilderTest.php │ │ ├── ManipulatorTest.php │ │ ├── ClassBuilderTest.php │ │ └── CodeBuilderTest.php │ │ ├── GenerationProjectTest.php │ │ └── EventGeneratorVisitorTest.php └── bootstrap.php ├── .travis.yml ├── bin └── generator.php ├── sandbox ├── dumper.php ├── generator_em.yml ├── generator.yml ├── xml │ ├── Entities.Address.dcm.xml │ └── Entities.User.dcm.xml └── doctrine-orm.php ├── phpunit.xml.dist ├── composer.json ├── lib └── Doctrine │ └── CodeGenerator │ ├── Builder │ ├── Visitor.php │ ├── AbstractBuilder.php │ ├── Manipulator.php │ ├── PropertyBuilder.php │ ├── MethodBuilder.php │ ├── ClassBuilder.php │ └── CodeBuilder.php │ ├── Listener │ ├── FluentSetterListener.php │ ├── AbstractCodeListener.php │ ├── GenerateClassesListener.php │ ├── GetterSetterListener.php │ ├── TimestampableListener.php │ ├── DocListener.php │ └── ORM │ │ ├── GenerateProjectListener.php │ │ └── CollectionListener.php │ ├── File.php │ ├── ProjectEvent.php │ ├── EventGenerator.php │ ├── GeneratorEvent.php │ ├── ProjectWriter.php │ ├── GenerationProject.php │ ├── EventGeneratorVisitor.php │ └── Command │ └── GenerateCommand.php ├── README.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | sandbox/code 2 | vendor 3 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/_files/test.php: -------------------------------------------------------------------------------- 1 | addCommands(array( 8 | new Doctrine\CodeGenerator\Command\GenerateCommand(), 9 | )); 10 | $console->run(); 11 | 12 | -------------------------------------------------------------------------------- /sandbox/dumper.php: -------------------------------------------------------------------------------- 1 | parse($code); 13 | 14 | var_dump($stmts); 15 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('Doctrine\Tests\CodeGenerator', __DIR__); 13 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | 10 | 11 | ./lib 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sandbox/generator_em.yml: -------------------------------------------------------------------------------- 1 | generator: 2 | destination: code 3 | listeners: 4 | Doctrine\CodeGenerator\Listener\ORM\GenerateProjectListener: ~ 5 | Doctrine\CodeGenerator\Listener\TimestampableListener: 6 | - "Entities\User" 7 | Doctrine\CodeGenerator\Listener\GetterSetterListener: ~ 8 | Doctrine\CodeGenerator\Listener\ORM\CollectionListener: ~ 9 | Doctrine\CodeGenerator\Listener\DocListener: ~ 10 | Doctrine\CodeGenerator\Listener\FluentSetterListener: ~ 11 | 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doctrine/code-generator", 3 | "require": { 4 | "nikic/php-parser": "dev-master", 5 | "symfony/console": "*", 6 | "symfony/yaml": "*", 7 | "doctrine/common": "*" 8 | }, 9 | "require-dev": { 10 | "doctrine/orm": "*" 11 | }, 12 | "suggest": { 13 | "doctrine/orm": "Code-Generation for the ORM" 14 | }, 15 | "autoload": { 16 | "psr-0": { 17 | "Doctrine\\CodeGenerator": "lib" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sandbox/generator.yml: -------------------------------------------------------------------------------- 1 | generator: 2 | destination: code 3 | listeners: 4 | Doctrine\CodeGenerator\Listener\GenerateClassesListener: 5 | classes: 6 | TestClass: 7 | properties: 8 | foo: ~ 9 | bar: ~ 10 | Doctrine\CodeGenerator\Listener\TimestampableListener: 11 | - "TestClass" 12 | Doctrine\CodeGenerator\Listener\GetterSetterListener: ~ 13 | Doctrine\CodeGenerator\Listener\DocListener: ~ 14 | Doctrine\CodeGenerator\Listener\FluentSetterListener: ~ 15 | -------------------------------------------------------------------------------- /sandbox/xml/Entities.Address.dcm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sandbox/doctrine-orm.php: -------------------------------------------------------------------------------- 1 | 'pdo_sqlite', 'memory' => true), $config); 10 | 11 | $helper = new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em); 12 | $set = new \Symfony\Component\Console\Helper\HelperSet(); 13 | $set->set($helper, 'em'); 14 | 15 | $console = new Application(); 16 | $console->setHelperSet($set); 17 | $console->addCommands(array( 18 | new Doctrine\CodeGenerator\Command\GenerateCommand(), 19 | )); 20 | $console->run(); 21 | 22 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/ProjectWriterTest.php: -------------------------------------------------------------------------------- 1 | dir = sys_get_temp_dir() . '/dcg'; 15 | } 16 | 17 | public function testWrite() 18 | { 19 | $project = new GenerationProject(); 20 | $project->getClass('stdClass'); 21 | 22 | $writer = new ProjectWriter($this->dir); 23 | $writer->write($project); 24 | 25 | $this->assertTrue(file_exists($this->dir . '/stdClass.php')); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/EventGeneratorTest.php: -------------------------------------------------------------------------------- 1 | getMock('Doctrine\Common\EventManager'); 15 | 16 | $evm->expects($this->once())->method('dispatchEvent') 17 | ->with('onStartGeneration', new ProjectEvent($project)); 18 | 19 | $generator = new EventGenerator($evm); 20 | $generator->generate($project); 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /sandbox/xml/Entities.User.dcm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/TestCase.php: -------------------------------------------------------------------------------- 1 | prettyPrint($node); 17 | 18 | $this->assertStringStartsWith($startsWith, $actual, $message); 19 | } 20 | 21 | public function assertCode($expected, $node, $message = null) 22 | { 23 | if (!is_array($node)) { 24 | $node = array($node); 25 | } 26 | 27 | $printer = new PHPParser_PrettyPrinter_Zend(); 28 | $actual = $printer->prettyPrint($node); 29 | 30 | $this->assertEquals($expected, $actual, $message); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/GenerateClassesListenerTest.php: -------------------------------------------------------------------------------- 1 | array( 16 | 'Test' => array( 17 | 'properties' => array('foo' => array(), 'bar' => array()) 18 | ) 19 | ) 20 | )); 21 | $listener->onStartGeneration(new ProjectEvent($project)); 22 | 23 | $builder = $project->getClass('Test'); 24 | 25 | $this->assertTrue($builder->hasProperty('foo')); 26 | $this->assertTrue($builder->hasProperty('bar')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/FluentSetterListenerTest.php: -------------------------------------------------------------------------------- 1 | onGenerateSetter(new GeneratorEvent($builder, $project)); 20 | 21 | $node = $builder->getNode(); 22 | $printer = new \PHPParser_PrettyPrinter_Zend(); 23 | 24 | $this->assertEquals(<<<'PHP' 25 | public function setFoo() 26 | { 27 | return $this; 28 | } 29 | PHP 30 | , $printer->prettyPrint(array($node))); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/GetterSetterListenerTest.php: -------------------------------------------------------------------------------- 1 | onGenerateProperty(new GeneratorEvent($builder, $project)); 20 | 21 | $node = $class->getNode(); 22 | $printer = new \PHPParser_PrettyPrinter_Zend(); 23 | 24 | $this->assertEquals(<<<'PHP' 25 | class Foo 26 | { 27 | public function setFoo($foo) 28 | { 29 | $this->foo = $foo; 30 | } 31 | public function getFoo() 32 | { 33 | return $this->foo; 34 | } 35 | } 36 | PHP 37 | , $printer->prettyPrint(array($node))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/Visitor.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | interface Visitor 23 | { 24 | public function visitClass(ClassBuilder $class); 25 | public function visitMethod(MethodBuilder $method); 26 | public function visitProperty(PropertyBuilder $property); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Builder/MethodBuilderTest.php: -------------------------------------------------------------------------------- 1 | assertEquals("test", $builder->getName()); 15 | } 16 | 17 | public function testGetClass() 18 | { 19 | $builder = new MethodBuilder("test", $class= new ClassBuilder("foo")); 20 | 21 | $this->assertSame($class, $builder->getClass()); 22 | } 23 | 24 | public function testGetNode() 25 | { 26 | $builder = new MethodBuilder("test", new ClassBuilder("foo")); 27 | $builder->param('foo'); 28 | 29 | $node = $builder->getNode(); 30 | 31 | $this->assertInstanceOf('PHPParser_Node_Stmt_ClassMethod', $node); 32 | $this->assertEquals('test', $node->name); 33 | $this->assertEquals('foo', $node->params[0]->name); 34 | } 35 | 36 | public function testVisit() 37 | { 38 | $builder = new MethodBuilder("test", new ClassBuilder("foo")); 39 | $visitor = $this->getMock('Doctrine\CodeGenerator\Builder\Visitor'); 40 | 41 | $visitor->expects($this->once())->method('visitMethod')->with($builder); 42 | 43 | $builder->visit($visitor); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/TimestampableListenerTest.php: -------------------------------------------------------------------------------- 1 | makeTimestampable($class); 16 | } 17 | 18 | public function testMakeClassTimestampableExistingConstructor() 19 | { 20 | $codeBuilder = new CodeBuilder(); 21 | $class = new ClassBuilder('Foo'); 22 | $constructor = $class->getMethod('__construct'); 23 | $constructor->append(array( 24 | $codeBuilder->assignment( 25 | $codeBuilder->instanceVariable('foo'), 26 | $codeBuilder->variable('bar') 27 | ) 28 | )); 29 | 30 | $listener = new TimestampableListener(); 31 | 32 | $listener->makeTimestampable($class); 33 | 34 | $node = $constructor->getNode(); 35 | 36 | $printer = new \PHPParser_PrettyPrinter_Zend(); 37 | 38 | $this->assertEquals(2, count($node->stmts)); 39 | $this->assertEquals(<<<'PHP' 40 | public function __construct() 41 | { 42 | $this->foo = $bar; 43 | $this->created = $this->updated = new \DateTime(); 44 | } 45 | PHP 46 | , $printer->prettyPrint(array($node))); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/GenerationProjectTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(0, count($project->getFiles())); 12 | } 13 | 14 | public function testGetClass() 15 | { 16 | $project = new GenerationProject(); 17 | 18 | $this->assertInstanceOf('Doctrine\CodeGenerator\Builder\ClassBuilder', $project->getClass('stdClass')); 19 | } 20 | 21 | public function testGetSameClass() 22 | { 23 | $project = new GenerationProject(); 24 | $classA = $project->getClass('stdClass'); 25 | $classB = $project->getClass('stdClass'); 26 | 27 | $this->assertSame($classA, $classB); 28 | } 29 | 30 | public function testGetClasses() 31 | { 32 | $project = new GenerationProject(); 33 | $classA = $project->getClass('classA'); 34 | $classB = $project->getClass('classB'); 35 | 36 | $this->assertEquals(2, count($project->getClasses())); 37 | } 38 | 39 | public function testGetFiles() 40 | { 41 | $project = new GenerationProject(); 42 | $classA = $project->getClass('stdClass'); 43 | 44 | $files = $project->getFiles(); 45 | $this->assertEquals(1, count($files)); 46 | $this->assertContainsOnly('Doctrine\CodeGenerator\File', $files); 47 | $this->assertEquals('stdClass.php', $files[0]->getPath()); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/FluentSetterListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\CodeGenerator\GeneratorEvent; 23 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 24 | 25 | /** 26 | * Add Fluent $this return to every generated setter. 27 | */ 28 | class FluentSetterListener extends AbstractCodeListener 29 | { 30 | public function onGenerateSetter(GeneratorEvent $event) 31 | { 32 | $node = $event->getNode(); 33 | $code = new CodeBuilder(); 34 | 35 | $node->append(array( 36 | $code->returnStmt($code->variable('this')) 37 | )); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/File.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use PHPParser_NodeAbstract; 23 | use PHPParser_NodeTraverser; 24 | use PHPParser_PrettyPrinterAbstract; 25 | 26 | class File 27 | { 28 | private $ast; 29 | private $path; 30 | 31 | public function __construct($path, array $ast) 32 | { 33 | $this->path = $path; 34 | $this->ast = $ast; 35 | } 36 | 37 | public function getPath() 38 | { 39 | return $this->path; 40 | } 41 | 42 | public function prettyPrint(PHPParser_PrettyPrinterAbstract $printer) 43 | { 44 | return $printer->prettyPrint($this->ast); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/ProjectEvent.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use Doctrine\Common\EventArgs; 23 | 24 | /** 25 | * Project-wide event arguments 26 | * 27 | * @author Benjamin Eberlei 28 | */ 29 | class ProjectEvent extends EventArgs 30 | { 31 | const onStartGeneration = 'onStartGeneration'; 32 | 33 | /** 34 | * @var GenerationProject 35 | */ 36 | private $project; 37 | 38 | public function __construct(GenerationProject $project) 39 | { 40 | $this->project = $project; 41 | } 42 | 43 | /** 44 | * @return GenerationProject 45 | */ 46 | public function getProject() 47 | { 48 | return $this->project; 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/ORM/CollectionListenerTest.php: -------------------------------------------------------------------------------- 1 | setAttribute('isAssociation', true); 17 | $builder->setAttribute('mapping', array( 18 | 'targetEntity' => 'stdClass', 19 | 'type' => \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_MANY 20 | )); 21 | 22 | $project = new GenerationProject(); 23 | $listener = new CollectionListener(); 24 | 25 | $listener->onGenerateProperty(new GeneratorEvent($builder, $project)); 26 | 27 | $class = $builder->getClass(); 28 | 29 | $this->assertTrue($class->hasMethod('addFoo')); 30 | $this->assertTrue($class->hasMethod('removeFoo')); 31 | 32 | $printer = new \PHPParser_PrettyPrinter_Zend(); 33 | $this->assertEquals(<<<'PHP' 34 | class Foo 35 | { 36 | public function __construct() 37 | { 38 | $this->foo = new \Doctrine\Common\Collections\ArrayCollection(); 39 | } 40 | public function addFoo(stdClass $foo) 41 | { 42 | $this->foo[] = $foo; 43 | } 44 | public function removeFoo(stdClass $foo) 45 | { 46 | $this->foo->removeElement($foo); 47 | } 48 | } 49 | PHP 50 | , $printer->prettyPrint(array($class->getNode()))); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Builder/PropertyBuilderTest.php: -------------------------------------------------------------------------------- 1 | assertEquals("foo", $builder->getName()); 15 | } 16 | 17 | public function testGetClass() 18 | { 19 | $builder = new PropertyBuilder("foo", $class = new ClassBuilder("Bar")); 20 | 21 | $this->assertSame($class, $builder->getClass()); 22 | } 23 | 24 | public function testSetGetAttribute() 25 | { 26 | $builder = new PropertyBuilder("foo", $class = new ClassBuilder("Bar")); 27 | 28 | $this->assertNull($builder->getAttribute('foo')); 29 | $builder->setAttribute('foo', 'bar'); 30 | $this->assertEquals('bar', $builder->getAttribute('foo')); 31 | } 32 | 33 | public function testSetGetDocComment() 34 | { 35 | $builder = new PropertyBuilder("foo", $class = new ClassBuilder("Bar")); 36 | 37 | $this->assertNull($builder->getDocComment()); 38 | $builder->setDocComment('bar'); 39 | $this->assertEquals('bar', $builder->getDocComment()); 40 | } 41 | 42 | public function testGetNode() 43 | { 44 | $builder = new PropertyBuilder("foo", new ClassBuilder("Bar")); 45 | $builder->makePublic()->makeStatic()->setDefault(false); 46 | 47 | $node = $builder->getNode(); 48 | 49 | $this->assertInstanceOf('PHPParser_Node_Stmt_Property', $node); 50 | $this->assertEquals('foo', $node->props[0]->name); 51 | $this->assertEquals('false', $node->props[0]->default->name->parts[0]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Builder/ManipulatorTest.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\Tests\CodeGenerator\Builder; 21 | 22 | use PHPParser_Node_Stmt_Class; 23 | use PHPParser_Node_Stmt_ClassMethod; 24 | use PHPParser_Node_Stmt_Property; 25 | use PHPParser_Builder_Property; 26 | use Doctrine\CodeGenerator\Builder\Manipulator; 27 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 28 | use Doctrine\Tests\CodeGenerator\TestCase; 29 | 30 | class ManipulatorTest extends TestCase 31 | { 32 | public function testSetDocComment() 33 | { 34 | $class = new PHPParser_Node_Stmt_Class("Test"); 35 | $manipulator = new Manipulator(); 36 | 37 | $manipulator->setDocComment($class, "Test!"); 38 | $manipulator->setDocComment($class, "Test1234!"); 39 | 40 | $this->assertEquals("Test1234!", $class->getDocComment()->getText()); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/EventGenerator.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use Doctrine\Common\EventManager; 23 | 24 | class EventGenerator 25 | { 26 | private $evm; 27 | 28 | public function __construct(EventManager $evm) 29 | { 30 | $this->evm = $evm; 31 | } 32 | 33 | public function generate(GenerationProject $project) 34 | { 35 | $visitor = new EventGeneratorVisitor($this->evm, $project); 36 | 37 | $this->evm->dispatchEvent(ProjectEvent::onStartGeneration, new ProjectEvent($project)); 38 | 39 | do { 40 | $cnt = count($visitor); 41 | 42 | // TODO: GenerationProject a "Builder" as well? 43 | foreach ($project->getClasses() as $class) { 44 | $class->visit($visitor); 45 | } 46 | } while ($cnt < count($visitor)); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/AbstractCodeListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\Common\EventSubscriber; 23 | use Doctrine\Common\EventManager; 24 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 25 | use Doctrine\CodeGenerator\MetadataContainer; 26 | use Doctrine\CodeGenerator\GenerationProject; 27 | 28 | abstract class AbstractCodeListener implements EventSubscriber 29 | { 30 | protected $eventManager; 31 | 32 | public function setEventManager(EventManager $eventManager) 33 | { 34 | $this->eventManager = $eventManager; 35 | } 36 | 37 | public function getSubscribedEvents() 38 | { 39 | $methods = get_class_methods($this); 40 | $eventMethods = array(); 41 | foreach ($methods as $method) { 42 | if (substr($method, 0, 2) === "on") { 43 | $eventMethods[] = $method; 44 | } 45 | } 46 | 47 | return $eventMethods; 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/GenerateClassesListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\CodeGenerator\GenerationProject; 23 | use Doctrine\CodeGenerator\Builder\ClassBuilder; 24 | use Doctrine\CodeGenerator\ProjectEvent; 25 | 26 | class GenerateClassesListener extends AbstractCodeListener 27 | { 28 | /** 29 | * @var array 30 | */ 31 | private $config; 32 | 33 | public function __construct(array $config) 34 | { 35 | $this->config = $config; 36 | } 37 | 38 | public function onStartGeneration(ProjectEvent $event) 39 | { 40 | $project = $event->getProject(); 41 | 42 | foreach ($this->config['classes'] as $className => $struct) { 43 | $class = $project->getClass($className); 44 | 45 | foreach ($struct['properties'] as $propertyName => $propertyStruct) { 46 | $property = $class->getProperty($propertyName); 47 | } 48 | } 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Listener/DocListenerTest.php: -------------------------------------------------------------------------------- 1 | setAttribute('type', 'DateTime'); 19 | 20 | $listener = new DocListener(); 21 | 22 | $listener->onGenerateProperty(new GeneratorEvent($builder, $project)); 23 | 24 | $this->assertEquals(<<getDocComment()); 30 | } 31 | 32 | public function testOnGenerateGetter() 33 | { 34 | $project = new GenerationProject(); 35 | $builder = new MethodBuilder("getFoo", new ClassBuilder("Foo")); 36 | 37 | $listener = new DocListener(); 38 | 39 | $listener->onGenerateGetter(new GeneratorEvent($builder, $project)); 40 | 41 | $this->assertEquals(<<getDocComment()); 49 | } 50 | 51 | public function testOnGenerateSetter() 52 | { 53 | $project = new GenerationProject(); 54 | $builder = new MethodBuilder("setFoo", new ClassBuilder("Foo")); 55 | 56 | $listener = new DocListener(); 57 | 58 | $listener->onGenerateSetter(new GeneratorEvent($builder, $project)); 59 | 60 | $this->assertEquals(<<<'PHP' 61 | /** 62 | * Set foo 63 | * 64 | * @param mixed $foo 65 | */ 66 | PHP 67 | , $builder->getDocComment()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/AbstractBuilder.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | abstract class AbstractBuilder 23 | { 24 | private $name; 25 | private $attributes; 26 | private $docComment; 27 | 28 | public function __construct($name) 29 | { 30 | $this->name = $name; 31 | } 32 | 33 | public function getName() 34 | { 35 | return $this->name; 36 | } 37 | 38 | public function setAttribute($name, $value) 39 | { 40 | $this->attributes[$name] = $value; 41 | return $this; 42 | } 43 | 44 | public function getAttribute($name) 45 | { 46 | if ( ! isset($this->attributes[$name])) { 47 | return null; 48 | } 49 | 50 | return $this->attributes[$name]; 51 | } 52 | 53 | public function setDocComment($comment) 54 | { 55 | $this->docComment = $comment; 56 | } 57 | 58 | public function getDocComment() 59 | { 60 | return $this->docComment; 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/GeneratorEvent.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use Doctrine\Common\EventArgs; 23 | 24 | /** 25 | * Generated PHP Code Event Arguments 26 | */ 27 | class GeneratorEvent extends EventArgs 28 | { 29 | const onGenerateClass = 'onGenerateClass'; 30 | const onGenerateProperty = 'onGenerateProperty'; 31 | const onGenerateMethod = 'onGenerateMethod'; 32 | const onGenerateGetter = 'onGenerateGetter'; 33 | const onGenerateSetter = 'onGenerateSetter'; 34 | const onGenerateConstructor = 'onGenerateConstructor'; 35 | const onGenerateFunction = 'onGenerateFunction'; 36 | const onGenerateInterface = 'onGenerateInterface'; 37 | const onGenerateTrait = 'onGenerateTrait'; 38 | const onGenerateParameter = 'onGenerateParameter'; 39 | 40 | private $node; 41 | private $project; 42 | 43 | public function __construct($node, $project) 44 | { 45 | $this->node = $node; 46 | $this->project = $project; 47 | } 48 | 49 | public function getNode() 50 | { 51 | return $this->node; 52 | } 53 | 54 | public function getProject() 55 | { 56 | return $this->project; 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/ProjectWriter.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use PHPParser_PrettyPrinterAbstract; 23 | use PHPParser_PrettyPrinter_Zend; 24 | 25 | /** 26 | * Generate project classes/functions to file-based code. 27 | * 28 | * Uses a PHPParser PrettyPrinter to do the formatting, 29 | * defaults to the Zend pretty printer. 30 | */ 31 | class ProjectWriter 32 | { 33 | private $printer; 34 | private $root; 35 | 36 | public function __construct($root, PHPParser_PrettyPrinterAbstract $printer = null) 37 | { 38 | $this->root = $root; 39 | $this->printer = $printer ?: new PHPParser_PrettyPrinter_Zend(); 40 | } 41 | 42 | public function write(GenerationProject $project) 43 | { 44 | foreach ($project->getFiles() as $file) { 45 | $this->writeFile($file); 46 | } 47 | } 48 | 49 | private function writeFile(File $file) 50 | { 51 | $path = $this->root . "/" . $file->getPath(); 52 | $dir = dirname($path); 53 | 54 | if (!file_exists($dir)) { 55 | mkdir($dir, 0777, true); 56 | } 57 | 58 | $code = "prettyPrint($this->printer); 59 | file_put_contents($path, $code); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/Manipulator.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | use PHPParser_BuilderAbstract; 23 | use PHPParser_Node_Stmt; 24 | use PHPParser_Node_Param; 25 | use PHPParser_Node_Stmt_Class; 26 | use PHPParser_Node_Stmt_Property; 27 | use PHPParser_Node_Stmt_ClassMethod; 28 | 29 | /** 30 | * Manipulator has API to change existing structures through convenience 31 | * methods. 32 | * 33 | * @author Benjamin Eberlei 34 | */ 35 | class Manipulator 36 | { 37 | /** 38 | * Set or overwrite the doc comment of a given node. 39 | * 40 | * @param PHPParser_Node $node 41 | * @param string $comment 42 | * @return void 43 | */ 44 | public function setDocComment(\PHPParser_Node $node, $comment) 45 | { 46 | if (!$comment) { 47 | return $node; 48 | } 49 | 50 | $doc = $node->getDocComment(); 51 | $comments = $node->getAttribute('comments'); 52 | 53 | if ($doc) { 54 | unset($comments[ count($comments) - 1]); 55 | } 56 | 57 | $comments[] = new \PHPParser_Comment_Doc($comment); 58 | $node->setAttribute('comments', array_values($comments)); 59 | 60 | return $node; 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/PropertyBuilder.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | use PHPParser_Builder_Property; 23 | 24 | class PropertyBuilder extends AbstractBuilder 25 | { 26 | private $builder; 27 | private $class; 28 | 29 | public function __construct($name, ClassBuilder $class) 30 | { 31 | parent::__construct($name); 32 | $this->class = $class; 33 | $this->builder = new PHPParser_Builder_Property($name); 34 | } 35 | 36 | public function getClass() 37 | { 38 | return $this->class; 39 | } 40 | 41 | public function makeStatic() 42 | { 43 | $this->builder->makeStatic(); 44 | return $this; 45 | } 46 | 47 | public function makePublic() 48 | { 49 | $this->builder->makePublic(); 50 | return $this; 51 | } 52 | 53 | public function makeProtected() 54 | { 55 | $this->builder->makeProtected(); 56 | return $this; 57 | } 58 | 59 | public function makePrivate() 60 | { 61 | $this->builder->makePrivate(); 62 | return $this; 63 | } 64 | 65 | public function setDefault($value) 66 | { 67 | $this->builder->setDefault($value); 68 | return $this; 69 | } 70 | 71 | public function getNode() 72 | { 73 | return $this->builder->getNode(); 74 | } 75 | 76 | public function visit(Visitor $visitor) 77 | { 78 | $visitor->visitProperty($this); 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/MethodBuilder.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | use PHPParser_Node_Stmt_ClassMethod; 23 | use PHPParser_Node_Param; 24 | use PHPParser_Builder_Method; 25 | 26 | class MethodBuilder extends AbstractBuilder 27 | { 28 | private $builder; 29 | private $class; 30 | private $params = array(); 31 | 32 | public function __construct($name, ClassBuilder $class) 33 | { 34 | parent::__construct($name); 35 | 36 | $this->class = $class; 37 | $this->builder = new PHPParser_Builder_Method($name); 38 | } 39 | 40 | public function getClass() 41 | { 42 | return $this->class; 43 | } 44 | 45 | public function append($stmts) 46 | { 47 | $this->builder->addStmts($stmts); 48 | return $this; 49 | } 50 | 51 | /** 52 | * @return MethodBuilder 53 | */ 54 | public function param($name, $default = null, $type = null, $byRef = false) 55 | { 56 | $param = new PHPParser_Node_Param($name, $default, $type, $byRef); 57 | $this->params[] = $param; 58 | return $this; 59 | } 60 | 61 | public function visit(Visitor $visitor) 62 | { 63 | $visitor->visitMethod($this); 64 | } 65 | 66 | /** 67 | * @return PHPParser_Node_Param 68 | */ 69 | public function getNode() 70 | { 71 | $node = $this->builder->getNode(); 72 | $node->params = $this->params; 73 | return $node; 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Builder/ClassBuilderTest.php: -------------------------------------------------------------------------------- 1 | assertEquals("PHP\\stdClass", $builder->getName()); 15 | $this->assertEquals("PHP", $builder->getNamespace()); 16 | } 17 | 18 | public function testGetNode() 19 | { 20 | $builder = new ClassBuilder("Foo"); 21 | $builder->extend("stdClass") 22 | ->implement("Jsonable") 23 | ->makeAbstract(); 24 | 25 | $node = $builder->getNode(); 26 | 27 | $this->assertInstanceOf('PHPParser_Node_Stmt_Class', $node); 28 | $this->assertEquals("Foo", $node->name); 29 | $this->assertEquals("stdClass", (string)$node->extends); 30 | $this->assertEquals("Jsonable", (string)$node->implements[0]); 31 | } 32 | 33 | public function testMethodBuilder() 34 | { 35 | $builder = new ClassBuilder("Foo"); 36 | 37 | $this->assertFalse($builder->hasMethod("test")); 38 | 39 | $method = $builder->getMethod("test"); 40 | 41 | $this->assertInstanceOf('Doctrine\CodeGenerator\Builder\MethodBuilder', $method); 42 | $this->assertTrue($builder->hasMethod("test")); 43 | $this->assertSame($method, $builder->getMethod("test")); 44 | } 45 | 46 | public function testPropertyBuilder() 47 | { 48 | $builder = new ClassBuilder("Foo"); 49 | 50 | $this->assertFalse($builder->hasProperty("test")); 51 | 52 | $property = $builder->getProperty("test"); 53 | 54 | $this->assertInstanceOf('Doctrine\CodeGenerator\Builder\PropertyBuilder', $property); 55 | $this->assertTrue($builder->hasProperty("test")); 56 | $this->assertSame($property, $builder->getProperty("test")); 57 | } 58 | 59 | public function testVisit() 60 | { 61 | $builder = new ClassBuilder("Foo"); 62 | $visitor = $this->getMock('Doctrine\CodeGenerator\Builder\Visitor'); 63 | 64 | $builder->getProperty("test"); 65 | $builder->getMethod("test"); 66 | 67 | $visitor->expects($this->at(0))->method('visitClass'); 68 | $visitor->expects($this->at(1))->method('visitProperty'); 69 | $visitor->expects($this->at(2))->method('visitMethod'); 70 | 71 | $builder->visit($visitor); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/GetterSetterListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\CodeGenerator\GeneratorEvent; 23 | use Doctrine\CodeGenerator\Builder\ClassBuilder; 24 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 25 | 26 | /** 27 | * Each property is turned to protected and getters/setters are added. 28 | */ 29 | class GetterSetterListener extends AbstractCodeListener 30 | { 31 | public function onGenerateProperty(GeneratorEvent $event) 32 | { 33 | $property = $event->getNode(); 34 | //$property->makeProtected(); 35 | 36 | $class = $property->getClass(); 37 | $code = new CodeBuilder(); 38 | 39 | $setName = 'set' . ucfirst($property->getName()); 40 | $getName = 'get' . ucfirst($property->getName()); 41 | 42 | if ($class->hasMethod($setName) || $class->hasMethod($getName)) { 43 | 44 | return; 45 | } 46 | 47 | $setter = $class->getMethod($setName); 48 | $setter->param($property->getName()); 49 | $setter->append(array( 50 | $code->assignment( 51 | $code->instanceVariable($property->getName()), $code->variable($property->getName()) 52 | ) 53 | )); 54 | $setter->setAttribute('property', $property); 55 | 56 | $getter = $class->getMethod($getName); 57 | $getter->append(array($code->returnStmt($code->instanceVariable($property->getName())))); 58 | $getter->setAttribute('property', $property); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/GenerationProject.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use Doctrine\CodeGenerator\Builder\ClassBuilder; 23 | 24 | /** 25 | * Code Generation project holds classes and functions to generate. 26 | * 27 | * During code generation you can add and modify classes and functions 28 | * to be generated. 29 | */ 30 | class GenerationProject 31 | { 32 | private $classes = array(); 33 | private $functions = array(); 34 | 35 | /** 36 | * @return ClassBuilder 37 | */ 38 | public function getClass($className) 39 | { 40 | if ( ! isset($this->classes[$className])) { 41 | $this->classes[$className] = new ClassBuilder($className); 42 | } 43 | 44 | return $this->classes[$className]; 45 | } 46 | 47 | public function getClasses() 48 | { 49 | return $this->classes; 50 | } 51 | 52 | public function getFunction($functionName) 53 | { 54 | throw new \BadMethodCallException("not implemented yet"); 55 | } 56 | 57 | public function getFiles() 58 | { 59 | $files = array(); 60 | 61 | foreach ($this->classes as $class) { 62 | $path = str_replace(array("\\", "_"), "/", $class->getName()) . ".php"; 63 | $stmts = array($class->getNode()); 64 | 65 | if ($class->getNamespace()) { 66 | $stmts = array( 67 | new \PHPParser_Node_Stmt_Namespace(new \PHPParser_Node_Name($class->getNamespace()), $stmts) 68 | ); 69 | } 70 | 71 | $files[] = new File($path, $stmts); 72 | } 73 | 74 | return $files; 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/Builder/CodeBuilderTest.php: -------------------------------------------------------------------------------- 1 | builder = new CodeBuilder(); 14 | } 15 | 16 | public function testInstanceVariable() 17 | { 18 | $expr = $this->builder->instanceVariable('foo'); 19 | 20 | $this->assertInstanceOf('PHPParser_Node_Expr_PropertyFetch', $expr); 21 | $this->assertInstanceOf('PHPParser_Node_Expr_Variable', $expr->var); 22 | $this->assertEquals('this', $expr->var->name); 23 | $this->assertEquals('foo', $expr->name); 24 | } 25 | 26 | public function testVariable() 27 | { 28 | $expr = $this->builder->variable('foo'); 29 | 30 | $this->assertInstanceOf('PHPParser_Node_Expr_Variable', $expr); 31 | $this->assertEquals('foo', $expr->name); 32 | } 33 | 34 | public function testReturn() 35 | { 36 | $stmt = $this->builder->returnStmt($this->builder->variable('foo')); 37 | 38 | $this->assertInstanceOf('PHPParser_Node_Stmt_Return', $stmt); 39 | } 40 | 41 | public function testAssignment() 42 | { 43 | $foo = $this->builder->variable('foo'); 44 | $bar = $this->builder->variable('bar'); 45 | $stmt = $this->builder->assignment($foo, $bar); 46 | 47 | $this->assertInstanceOf('PHPParser_Node_Expr_Assign', $stmt); 48 | $this->assertSame($stmt->var, $foo); 49 | $this->assertSame($stmt->expr, $bar); 50 | } 51 | 52 | public function testInstantiate() 53 | { 54 | $expr = $this->builder->instantiate('DateTime'); 55 | 56 | $this->assertInstanceOf('PHPParser_Node_Expr_New', $expr); 57 | } 58 | 59 | public function testCode() 60 | { 61 | $exprs = $this->builder->code('$foo'); 62 | 63 | $this->assertEquals(1, count($exprs)); 64 | $this->assertInstanceOf('PHPParser_Node_Expr_Variable', $exprs[0]); 65 | $this->assertEquals('foo', $exprs[0]->name); 66 | } 67 | 68 | public function testClassCode() 69 | { 70 | $exprs = $this->builder->classCode(<<assertEquals(2, count($exprs)); 78 | $this->assertInstanceOf('PHPParser_Node_Stmt_Property', $exprs[0]); 79 | $this->assertInstanceOf('PHPParser_Node_Stmt_ClassMethod', $exprs[1]); 80 | } 81 | 82 | public function testClassBuilder() 83 | { 84 | $builder = $this->builder->classBuilder('Foo'); 85 | 86 | $this->assertInstanceOf('Doctrine\CodeGenerator\Builder\ClassBuilder', $builder); 87 | $this->assertEquals('Foo', $builder->getNode()->name); 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/TimestampableListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\CodeGenerator\GeneratorEvent; 23 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 24 | 25 | /** 26 | * Turns classes into timestampable classes. 27 | * 28 | * @author Benjamin Eberlei 29 | */ 30 | class TimestampableListener extends AbstractCodeListener 31 | { 32 | private $classes; 33 | 34 | public function __construct(array $classes = array()) 35 | { 36 | $this->classes = $classes; 37 | } 38 | 39 | public function onGenerateClass(GeneratorEvent $event) 40 | { 41 | $class = $event->getNode(); 42 | 43 | if ( ! in_array($class->getName(), $this->classes)) { 44 | return; 45 | } 46 | 47 | $this->makeTimestampable($class); 48 | } 49 | 50 | public function makeTimestampable($class) 51 | { 52 | $code = new CodeBuilder(); 53 | $constructor = $class->getMethod('__construct'); 54 | 55 | $class->getProperty('updated')->makeProtected()->setAttribute('type', 'DateTime'); 56 | $class->getProperty('created')->makeProtected()->setAttribute('type', 'DateTime'); 57 | 58 | $constructor->append(array( 59 | $code->assignment( 60 | $code->instanceVariable('created'), 61 | $code->assignment( 62 | $code->instanceVariable('updated'), 63 | $code->instantiate('DateTime') 64 | ) 65 | ) 66 | )); 67 | 68 | $class->append($code->classCode(<<created; 72 | } 73 | 74 | public function setUpdated(\DateTime \$date) 75 | { 76 | \$this->updated = \$date; 77 | } 78 | 79 | public function getUpdated() 80 | { 81 | return \$this->updated; 82 | } 83 | ETS 84 | )) 85 | ; 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/EventGeneratorVisitor.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator; 21 | 22 | use Doctrine\CodeGenerator\Builder\Visitor; 23 | use Doctrine\CodeGenerator\Builder\ClassBuilder; 24 | use Doctrine\CodeGenerator\Builder\MethodBuilder; 25 | use Doctrine\CodeGenerator\Builder\PropertyBuilder; 26 | use Doctrine\CodeGenerator\Builder\FunctionBuilder; 27 | use Doctrine\Common\EventManager; 28 | 29 | use Countable; 30 | use SplObjectStorage; 31 | 32 | class EventGeneratorVisitor implements Visitor, Countable 33 | { 34 | private $visited; 35 | private $project; 36 | private $evm; 37 | 38 | public function __construct(EventManager $evm, GenerationProject $project) 39 | { 40 | $this->evm = $evm; 41 | $this->project = $project; 42 | $this->visited = new SplObjectStorage(); 43 | } 44 | 45 | public function visitClass(ClassBuilder $class) 46 | { 47 | $this->dispatch($class, "onGenerateClass"); 48 | } 49 | 50 | public function visitMethod(MethodBuilder $method) 51 | { 52 | $methodName = $method->getName(); 53 | 54 | if (substr($methodName, 0, 3) === "set") { 55 | $eventName = "onGenerateSetter"; 56 | } else if (substr($methodName, 0, 3) === "get") { 57 | $eventName = "onGenerateGetter"; 58 | } else if ($methodName === "__construct") { 59 | $eventName = "onGenerateConstructor"; 60 | } else { 61 | $eventName = "onGenerateMethod"; 62 | } 63 | 64 | $this->dispatch($method, $eventName); 65 | } 66 | 67 | public function visitProperty(PropertyBuilder $property) 68 | { 69 | $this->dispatch($property, "onGenerateProperty"); 70 | } 71 | 72 | private function dispatch($builder, $eventName) 73 | { 74 | if ($this->visited->contains($builder)) { 75 | return; 76 | } 77 | 78 | $this->visited->attach($builder); 79 | 80 | $this->evm->dispatchEvent($eventName, new GeneratorEvent($builder, $this->project)); 81 | } 82 | 83 | public function count() 84 | { 85 | return count($this->visited); 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /tests/Doctrine/Tests/CodeGenerator/EventGeneratorVisitorTest.php: -------------------------------------------------------------------------------- 1 | getMock('Doctrine\Common\EventManager'); 17 | $project = new GenerationProject(); 18 | $visitor = new EventGeneratorVisitor($evm, $project); 19 | 20 | $class = new ClassBuilder('stdClass'); 21 | $evm->expects($this->once())->method('dispatchEvent')->with('onGenerateClass', new GeneratorEvent($class, $project)); 22 | 23 | $visitor->visitClass($class); 24 | $visitor->visitClass($class); 25 | 26 | $this->assertEquals(1, count($visitor)); 27 | } 28 | 29 | public function testVisitMethod() 30 | { 31 | $evm = $this->getMock('Doctrine\Common\EventManager'); 32 | $project = new GenerationProject(); 33 | $visitor = new EventGeneratorVisitor($evm, $project); 34 | 35 | $method = new MethodBuilder('stdMethod', new ClassBuilder('foo')); 36 | $evm->expects($this->once())->method('dispatchEvent')->with('onGenerateMethod', new GeneratorEvent($method, $project)); 37 | 38 | $visitor->visitMethod($method); 39 | $visitor->visitMethod($method); 40 | 41 | $this->assertEquals(1, count($visitor)); 42 | } 43 | 44 | /** 45 | * @dataProvider dataVisitSpecialMethod 46 | */ 47 | public function testVisitSpecialMethod($method, $expectedEventName) 48 | { 49 | $evm = $this->getMock('Doctrine\Common\EventManager'); 50 | $project = new GenerationProject(); 51 | $visitor = new EventGeneratorVisitor($evm, $project); 52 | 53 | $method = new MethodBuilder($method, new ClassBuilder('foo')); 54 | $evm->expects($this->once())->method('dispatchEvent')->with($expectedEventName, new GeneratorEvent($method, $project)); 55 | 56 | $visitor->visitMethod($method); 57 | } 58 | 59 | public function dataVisitSpecialMethod() 60 | { 61 | return array( 62 | array('setFoo', 'onGenerateSetter'), 63 | array('getFoo', 'onGenerateGetter'), 64 | array('__construct', 'onGenerateConstructor'), 65 | ); 66 | } 67 | 68 | public function testVisitProperty() 69 | { 70 | $evm = $this->getMock('Doctrine\Common\EventManager'); 71 | $project = new GenerationProject(); 72 | $visitor = new EventGeneratorVisitor($evm, $project); 73 | 74 | $property = new PropertyBuilder('stdProperty', new ClassBuilder('foo')); 75 | $evm->expects($this->once())->method('dispatchEvent')->with('onGenerateProperty', new GeneratorEvent($property, $project)); 76 | 77 | $visitor->visitProperty($property); 78 | $visitor->visitProperty($property); 79 | 80 | $this->assertEquals(1, count($visitor)); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Doctrine Code Generator 2 | 3 | In general code-generation just moves the inability to correctly abstract code into a layer that simplifies the downsides of writing lots of code by hand. However there are use-cases for code-generation, mostly in context with Object-Relational-Mappers: 4 | 5 | * Transforming existing models (XML, UML, CSV, whatever) from one to another representation. 6 | * Generating boiler-plate code that is impossible to abstract nicely (`__get`/`__set`/`__call` alternatives) 7 | * Generating small bits of repetitive code that is not abstractable in PHP like specialized getters/setters, bidirectional method handlers. 8 | 9 | However the current approach for code-generation in Doctrine is fail. A single class organizes the code generation and has no sane means for extension. Templating based code-generation mechanism come into mind, but they only make the problem larger. Templates are not useful for code-generation because they dont offer manipulation and composition of different concerns, they only allow a linear direction of templates that are stacked on top of each other. 10 | 11 | Proper Code-Generation uses an AST that can be manipulated by the developer at any given point during the process. Manipulation of the AST can be triggered by events. The code-generator is feed by multiple sources of input that all operate on a large AST. The first source builds the general class layout, the second one adds to it and so on. The order of input given to the AST generator is relevant. It should be possible to write compiling/running code and then link this into the generated source code as if you literally write "traits". This should feel sort of like aspect-oriented programming where you designate some piece of code to be "used" by the code-generator during the generation process. 12 | 13 | The problem of code-generators is that they leave you with thousands of lines of untested code that is to be integrated in your application. This often leaves you with a huge gap of tested to untested code that is impossible to close. 14 | 15 | ## Idea 16 | 17 | Using [nikics PHP Parser](https://github.com/nikic/PHP_Parser) library we generate code using an AST. The code is generated from a set of input sources, usually during the "onStartGeneration" event. Events are subsequently triggered when code blocks are generated: 18 | 19 | * StartGeneration 20 | * Class 21 | * Property 22 | * Method 23 | * GetterMethod 24 | * SetterMethod 25 | * Constructor 26 | * Function 27 | 28 | Event Handlers can register to all events and for example: 29 | 30 | * Manipulate the AST 31 | * Trigger more specialized events 32 | 33 | A configuration for the code-generator would look like: 34 | 35 | generator: 36 | destination: "code" 37 | events: 38 | Doctrine\CodeGenerator\Listener\ORM\GenerateProjectListener: ~ 39 | Doctrine\CodeGenerator\Listener\GetterSetterListener: ~ 40 | Doctrine\CodeGenerator\Listener\ReadOnlyEntityValueObjectListener: ~ 41 | Doctrine\CodeGenerator\Listener\BidirectionalAssociationListener: ~ 42 | Doctrine\CodeGenerator\Listener\DocBlockListener: ~ 43 | Doctrine\CodeGenerator\Listener\DoctrineAnnotations: ~ 44 | Doctrine\CodeGenerator\Listener\ImmutableObjects: 45 | - "ImmutableClass1" 46 | Doctrine\CodeGenerator\Listener\ORM\MappingGenerator: 47 | type: xml 48 | output: 49 | codingStandard: Symfony 50 | 51 | ## Generating Code 52 | 53 | This project provides a bunch of builder objects on top of the PHP Parser Builder API. The aim is to have a fluent and 54 | convenient API to generate code. 55 | 56 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/DocListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener; 21 | 22 | use Doctrine\CodeGenerator\Builder\Manipulator; 23 | use Doctrine\CodeGenerator\GeneratorEvent; 24 | 25 | /** 26 | * Each property is turned to protected and getters/setters are added. 27 | */ 28 | class DocListener extends AbstractCodeListener 29 | { 30 | public function onGenerateProperty(GeneratorEvent $event) 31 | { 32 | $node = $event->getNode(); 33 | $type = $node->getAttribute('type') ?: 'mixed'; 34 | 35 | $node->setDocComment(<<getNode(); 46 | $type = $this->getMethodsPropertyType($node); 47 | $propertyName = $this->getPropertyName($node); 48 | 49 | $node->setDocComment(<<getNode(); 62 | $type = $this->getMethodsPropertyType($node); 63 | $propertyName = $this->getPropertyName($node); 64 | 65 | $node->setDocComment(<<getAttribute('property'); 78 | $type = null; 79 | 80 | if ($property) { 81 | $type = $property->getAttribute('type'); 82 | } else if (preg_match('(^(set|get)(.*+)$)', $method->getName(), $match)) { 83 | $propertyName = lcfirst($match[2]); 84 | $class = $method->getClass(); 85 | 86 | if ( ! $class->hasProperty($propertyName)) { 87 | return 'mixed'; 88 | } 89 | 90 | $property = $class->getProperty($propertyName); 91 | $type = $property->getAttribute('type'); 92 | } 93 | 94 | return $type ?: 'mixed'; 95 | } 96 | 97 | private function getPropertyName($node) 98 | { 99 | $property = $node->getAttribute('property'); 100 | 101 | if ($property) { 102 | return $property->getName(); 103 | } 104 | 105 | return lcfirst(substr($node->getName(), 3)); 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/ORM/GenerateProjectListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener\ORM; 21 | 22 | use Doctrine\CodeGenerator\GenerationProject; 23 | use Doctrine\CodeGenerator\Builder\ClassBuilder; 24 | use Doctrine\CodeGenerator\Listener\AbstractCodeListener; 25 | use Doctrine\CodeGenerator\ProjectEvent; 26 | 27 | use Doctrine\ORM\EntityManager; 28 | use Doctrine\ORM\Mapping\ClassMetadataInfo; 29 | use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; 30 | use Doctrine\DBAL\Types\Type; 31 | 32 | /** 33 | * Generate ORM Classes from Database or Schema data 34 | */ 35 | class GenerateProjectListener extends AbstractCodeListener 36 | { 37 | private $metadataFactory; 38 | 39 | private $typeAlias = array( 40 | Type::DATETIMETZ => '\DateTime', 41 | Type::DATETIME => '\DateTime', 42 | Type::DATE => '\DateTime', 43 | Type::TIME => '\DateTime', 44 | Type::OBJECT => '\stdClass', 45 | Type::BIGINT => 'integer', 46 | Type::SMALLINT => 'integer', 47 | Type::TEXT => 'string', 48 | Type::BLOB => 'resource', 49 | Type::DECIMAL => 'float', 50 | ); 51 | 52 | public function __construct(array $typeAlias = null) 53 | { 54 | $this->typeAlias = array_merge($this->typeAlias, $typeAlias ?: array()); 55 | } 56 | 57 | public function injectMetadataFactory($metadata) 58 | { 59 | $this->metadataFactory = $metadata; 60 | } 61 | 62 | public function onStartGeneration(ProjectEvent $event) 63 | { 64 | $project = $event->getProject(); 65 | 66 | foreach ($this->metadataFactory->getAllMetadata() as $metadata) { 67 | $class = $project->getClass($metadata->name); 68 | $this->generateClass($class, $metadata); 69 | } 70 | } 71 | 72 | /** 73 | * Generate a Class Node for the given Metadata 74 | * 75 | * @param ClassMetadataInfo $metadata 76 | * @return PHPParser_Node_Stmt_Class 77 | */ 78 | public function generateClass($class, ClassMetadataInfo $metadata) 79 | { 80 | foreach ($metadata->fieldMappings as $fieldName => $fieldMapping) { 81 | $property = $class->getProperty($fieldName); 82 | 83 | $property->setAttribute('isColumn', true); 84 | $property->setAttribute('type', isset($this->typeAlias[$fieldMapping['type']]) ? $this->typeAlias[$fieldMapping['type']] : $fieldMapping['type']); 85 | $property->setAttribute('mapping', $fieldMapping); 86 | } 87 | 88 | foreach ($metadata->associationMappings as $assocName => $assoc) { 89 | $property = $class->getProperty($assocName); 90 | 91 | $property->setAttribute('isAssociation', true); 92 | $property->setAttribute('type', $assoc['targetEntity']); 93 | $property->setAttribute('mapping', $assoc); 94 | } 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Command/GenerateCommand.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Command; 21 | 22 | use Symfony\Component\Console\Input\InputArgument; 23 | use Symfony\Component\Console\Input\InputOption; 24 | use Symfony\Component\Console\Input\InputInterface; 25 | use Symfony\Component\Console\Output\OutputInterface; 26 | use Symfony\Component\Console\Output\Output; 27 | use Symfony\Component\Console\Command\Command; 28 | use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; 29 | use Doctrine\CodeGenerator\ProjectEvent; 30 | use Doctrine\CodeGenerator\ProjectWriter; 31 | use Doctrine\CodeGenerator\EventGenerator; 32 | 33 | class GenerateCommand extends Command 34 | { 35 | protected function configure() 36 | { 37 | $this 38 | ->setDefinition(array( 39 | new InputArgument('configfile', InputArgument::REQUIRED, 'The generator.yml file path'), 40 | )) 41 | ->setName('code:generate') 42 | ->setDescription('Generate code') 43 | ->setHelp(<<code:generator command generates php code from the specified generator.yml 45 | project description. 46 | 47 | EOF 48 | ); 49 | } 50 | 51 | protected function execute(InputInterface $input, OutputInterface $output) 52 | { 53 | $path = realpath($input->getArgument('configfile')); 54 | $config = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($path)); 55 | 56 | if ( ! isset($config['generator'])) { 57 | throw new \RuntimeException; 58 | } 59 | 60 | $destination = realpath(dirname($path)) . "/". $config['generator']['destination']; 61 | if ( ! file_exists($destination)) { 62 | mkdir($destination, 0777, true); 63 | } 64 | 65 | $evm = new \Doctrine\Common\EventManager; 66 | $project = new \Doctrine\CodeGenerator\GenerationProject(); 67 | 68 | foreach ($config['generator']['listeners'] as $listener => $args) { 69 | if ( ! is_subclass_of($listener, 'Doctrine\CodeGenerator\Listener\AbstractCodeListener')) { 70 | throw new \RuntimeException("Listener $listener has to extend AbstractCodeListener"); 71 | } 72 | 73 | $listener = new $listener($args); 74 | $listener->setEventManager($evm); 75 | 76 | $evm->addEventSubscriber($listener); 77 | 78 | if (method_exists($listener, 'injectMetadataFactory')) { 79 | // HACK! 80 | $em = $this->getHelper('em')->getEntityManager(); 81 | $metadata = new DisconnectedClassMetadataFactory(); 82 | $metadata->setEntityManager($em); 83 | $listener->injectMetadataFactory($metadata); 84 | } 85 | } 86 | 87 | $eventGenerator = new EventGenerator($evm); 88 | $eventGenerator->generate($project); 89 | 90 | $writer = new ProjectWriter($destination); 91 | $writer->write($project); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Listener/ORM/CollectionListener.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Listener\ORM; 21 | 22 | use Doctrine\CodeGenerator\Listener\AbstractCodeListener; 23 | use Doctrine\CodeGenerator\Builder\CodeBuilder; 24 | use Doctrine\ORM\Mapping\ClassMetadataInfo; 25 | 26 | class CollectionListener extends AbstractCodeListener 27 | { 28 | public function onGenerateProperty($event) 29 | { 30 | $property = $event->getNode(); 31 | 32 | if ( ! $this->isToManyAssocation($property)) { 33 | return; 34 | } 35 | 36 | $this->addConstructorInitialization($property); 37 | $this->addAdderMethod($property); 38 | $this->addRemoverMethod($property); 39 | } 40 | 41 | private function addConstructorInitialization($property) 42 | { 43 | $class = $property->getClass(); 44 | $constructor = $class->getMethod('__construct'); 45 | $code = new CodeBuilder(); 46 | 47 | $constructor->append(array( 48 | $code->assignment( 49 | $code->instanceVariable($property->getName()), 50 | $code->instantiate('Doctrine\Common\Collections\ArrayCollection') 51 | ) 52 | )); 53 | } 54 | 55 | private function addAdderMethod($property) 56 | { 57 | $class = $property->getClass(); 58 | $addMethod = 'add' . ucfirst($property->getName()); 59 | 60 | if ($class->hasMethod($addMethod)) { 61 | return; 62 | } 63 | 64 | $mapping = $property->getAttribute('mapping'); 65 | $code = new CodeBuilder(); 66 | $adder = $class->getMethod($addMethod); 67 | $adder->param($property->getName(), null, $mapping['targetEntity']); 68 | $adder->append(array( 69 | $code->assignment( 70 | $code->arrayDimFetch($code->instanceVariable($property->getName())), 71 | $code->variable($property->getName()) 72 | ) 73 | )); 74 | $adder->setAttribute('property', $property); 75 | } 76 | 77 | private function addRemoverMethod($property) 78 | { 79 | $class = $property->getClass(); 80 | $removeMethod = 'remove' . ucfirst($property->getName()); 81 | 82 | if ($class->hasMethod($removeMethod)) { 83 | return; 84 | } 85 | 86 | $mapping = $property->getAttribute('mapping'); 87 | $code = new CodeBuilder(); 88 | $remover = $class->getMethod($removeMethod); 89 | $remover->param($property->getName(), null, $mapping['targetEntity']); 90 | $remover->append(array( 91 | $code->methodCall( 92 | $code->instanceVariable($property->getName()), 93 | 'removeElement', 94 | array($property->getName()) 95 | ) 96 | )); 97 | } 98 | 99 | private function isToManyAssocation($propertyNode) 100 | { 101 | $mapping = $propertyNode->getAttribute('mapping'); 102 | return $propertyNode->getAttribute('isAssociation') && 103 | ($mapping['type'] & ClassMetadataInfo::TO_MANY) > 0; 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/ClassBuilder.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | use PHPParser_Builder_Class; 23 | 24 | /** 25 | * Class Builder 26 | */ 27 | class ClassBuilder extends AbstractBuilder 28 | { 29 | private $name; 30 | private $namespace; 31 | private $builder; 32 | private $properties = array(); 33 | private $constants = array(); 34 | private $methods = array(); 35 | 36 | public function __construct($name) 37 | { 38 | parent::__construct($name); 39 | 40 | $namespace = false; 41 | if (strpos($name, "\\") !== false) { 42 | $parts = explode("\\", $name); 43 | $name = array_pop($parts); 44 | $namespace = implode("\\", $parts); 45 | } 46 | 47 | $this->builder = new PHPParser_Builder_Class($name); 48 | $this->namespace = $namespace; 49 | } 50 | 51 | public function getNamespace() 52 | { 53 | return $this->namespace; 54 | } 55 | 56 | public function extend($parentClassName) 57 | { 58 | $this->builder->extend($parentClassName); 59 | return $this; 60 | } 61 | 62 | public function implement($interfaceName) 63 | { 64 | $this->builder->implement($interfaceName); 65 | return $this; 66 | } 67 | 68 | public function makeAbstract() 69 | { 70 | $this->builder->makeAbstract(); 71 | return $this; 72 | } 73 | 74 | public function makeFinal() 75 | { 76 | $this->builder->makeFinal(); 77 | return $this; 78 | } 79 | 80 | /** 81 | * Does the class have the given method? 82 | * 83 | * @param string $name 84 | * @return bool 85 | */ 86 | public function hasMethod($name) 87 | { 88 | return isset($this->methods[$name]); 89 | } 90 | 91 | /** 92 | * @param string $name 93 | * @return MethodBuilder 94 | */ 95 | public function getMethod($name) 96 | { 97 | if ( ! isset($this->methods[$name])) { 98 | $this->methods[$name] = new MethodBuilder($name, $this); 99 | } 100 | 101 | return $this->methods[$name]; 102 | } 103 | 104 | /** 105 | * Does the class has the property? 106 | * 107 | * @param string $name 108 | * @return bool 109 | */ 110 | public function hasProperty($name) 111 | { 112 | return isset($this->properties[$name]); 113 | } 114 | 115 | /** 116 | * @param string $name 117 | * @return PropertyBuilder 118 | */ 119 | public function getProperty($name) 120 | { 121 | if ( ! $this->hasProperty($name)) { 122 | $this->properties[$name] = new PropertyBuilder($name, $this); 123 | } 124 | 125 | return $this->properties[$name]; 126 | } 127 | 128 | public function append($stmts) 129 | { 130 | $this->builder->addStmts($stmts); 131 | return $this; 132 | } 133 | 134 | /** 135 | * @return PHPParser_Node_Stmt_Class 136 | */ 137 | public function getNode() 138 | { 139 | $manipulator = new Manipulator(); 140 | foreach ($this->properties as $property) { 141 | $property = $manipulator->setDocComment($property->getNode(), $property->getDocComment()); 142 | $this->builder->addStmt($property); 143 | } 144 | 145 | foreach ($this->methods as $method) { 146 | $method = $manipulator->setDocComment($method->getNode(), $method->getDocComment()); 147 | $this->builder->addStmt($method); 148 | } 149 | 150 | return $manipulator->setDocComment($this->builder->getNode(), $this->getDocComment()); 151 | } 152 | 153 | public function visit(Visitor $visitor) 154 | { 155 | $visitor->visitClass($this); 156 | 157 | foreach ($this->properties as $property) { 158 | $visitor->visitProperty($property); 159 | } 160 | 161 | foreach ($this->methods as $method) { 162 | $visitor->visitMethod($method); 163 | } 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /lib/Doctrine/CodeGenerator/Builder/CodeBuilder.php: -------------------------------------------------------------------------------- 1 | . 18 | */ 19 | 20 | namespace Doctrine\CodeGenerator\Builder; 21 | 22 | use PHPParser_Parser; 23 | use PHPParser_Lexer; 24 | 25 | /** 26 | * CodeBuilder that simplifies generation of common PHP statements. 27 | * 28 | * @author Benjamin Eberlei 29 | */ 30 | class CodeBuilder 31 | { 32 | private $parser; 33 | 34 | public function __construct(PHPParser_Parser $parser = null) 35 | { 36 | $this->parser = $parser ?: new PHPParser_Parser(new PHPParser_Lexer); 37 | } 38 | 39 | /** 40 | * Return a class-builder for a new class with the given name. 41 | * 42 | * @param string $className 43 | * @return ClassBuilder 44 | */ 45 | public function classBuilder($className) 46 | { 47 | return new ClassBuilder($className); 48 | } 49 | 50 | /** 51 | * @return \Doctrine\CodeGenerator\Builder\MethodBuilder 52 | */ 53 | public function method($name) 54 | { 55 | return new MethodBuilder($name); 56 | } 57 | 58 | public function property($name) 59 | { 60 | return new \PHPParser_Builder_Property($name); 61 | } 62 | 63 | /** 64 | * @param string $name 65 | * @return PHPParser_Node_Expr_PropertyFetch 66 | */ 67 | public function instanceVariable($name) 68 | { 69 | return new \PHPParser_Node_Expr_PropertyFetch( 70 | $this->variable('this'), $name 71 | ); 72 | } 73 | 74 | /** 75 | * @return PHPParser_Node_Expr_Variable 76 | */ 77 | public function variable($name) 78 | { 79 | return new \PHPParser_Node_Expr_Variable($name); 80 | } 81 | 82 | /** 83 | * @return PHPParser_Node_Stmt_Return 84 | */ 85 | public function returnStmt($expr) 86 | { 87 | return new \PHPParser_Node_Stmt_Return($expr); 88 | } 89 | 90 | /** 91 | * @return PHPParser_Node_Expr_Assign 92 | */ 93 | public function assignment($left, $right) 94 | { 95 | return new \PHPParser_Node_Expr_Assign($left, $right); 96 | } 97 | 98 | /** 99 | * Return array of statements/expressions. 100 | * 101 | * @param string $string 102 | * @return array 103 | */ 104 | public function code($string) 105 | { 106 | return $this->parser->parse(""); 107 | } 108 | 109 | /** 110 | * Return array of statements/expressions in class context. 111 | * 112 | * If you want to generate expresions for properties/methods of a class. 113 | * 114 | * @param string $string 115 | * @return array 116 | */ 117 | public function classCode($string) 118 | { 119 | $stmts = $this->parser->parse(""); 120 | return $stmts[0]->stmts; 121 | } 122 | 123 | /** 124 | * Generate an instantiate class expression from a given className. 125 | * 126 | * @param string $className 127 | * @return PHPParser_Node_Expr_New 128 | */ 129 | public function instantiate($className) 130 | { 131 | return new \PHPParser_Node_Expr_New(new \PHPParser_Node_Name_FullyQualified($className)); 132 | } 133 | 134 | /** 135 | * @return PHPParser_Node_Expr_ArrayDimFetch 136 | */ 137 | public function arrayDimFetch($variable) 138 | { 139 | return new \PHPParser_Node_Expr_ArrayDimFetch($variable); 140 | } 141 | 142 | /** 143 | * @return PHPParser_Node_Expr_MethodCall 144 | */ 145 | public function methodCall($variable, $methodName, array $args = array()) 146 | { 147 | foreach ($args as $idx => $arg) { 148 | if (is_string($arg)) { 149 | $args[$idx] = new \PHPParser_Node_Arg($this->variable($arg)); 150 | } 151 | } 152 | 153 | return new \PHPParser_Node_Expr_MethodCall($variable, $methodName, $args); 154 | } 155 | 156 | /** 157 | * @return PHPParser_Node_Expr_MethodCall 158 | */ 159 | public function localMethodCall($methodName, array $args = array()) 160 | { 161 | return $this->methodCall($this->variable('this'), $methodName, $args); 162 | } 163 | } 164 | 165 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "87c228dbb19446cecfbdfe2444fa1f71", 3 | "packages": [ 4 | { 5 | "name": "doctrine/common", 6 | "version": "2.3.0", 7 | "source": { 8 | "type": "git", 9 | "url": "https://github.com/doctrine/common", 10 | "reference": "2.3.0" 11 | }, 12 | "dist": { 13 | "type": "zip", 14 | "url": "https://github.com/doctrine/common/zipball/2.3.0", 15 | "reference": "2.3.0", 16 | "shasum": "" 17 | }, 18 | "require": { 19 | "php": ">=5.3.2" 20 | }, 21 | "time": "2012-09-20 05:55:18", 22 | "type": "library", 23 | "extra": { 24 | "branch-alias": { 25 | "dev-master": "2.3.x-dev" 26 | } 27 | }, 28 | "installation-source": "dist", 29 | "autoload": { 30 | "psr-0": { 31 | "Doctrine\\Common": "lib/" 32 | } 33 | }, 34 | "license": [ 35 | "MIT" 36 | ], 37 | "authors": [ 38 | { 39 | "name": "Jonathan Wage", 40 | "email": "jonwage@gmail.com", 41 | "homepage": "http://www.jwage.com/" 42 | }, 43 | { 44 | "name": "Guilherme Blanco", 45 | "email": "guilhermeblanco@gmail.com", 46 | "homepage": "http://www.instaclick.com" 47 | }, 48 | { 49 | "name": "Roman Borschel", 50 | "email": "roman@code-factory.org" 51 | }, 52 | { 53 | "name": "Benjamin Eberlei", 54 | "email": "kontakt@beberlei.de" 55 | }, 56 | { 57 | "name": "Johannes M. Schmitt", 58 | "email": "schmittjoh@gmail.com", 59 | "homepage": "http://jmsyst.com", 60 | "role": "Developer of wrapped JMSSerializerBundle" 61 | } 62 | ], 63 | "description": "Common Library for Doctrine projects", 64 | "homepage": "http://www.doctrine-project.org", 65 | "keywords": [ 66 | "collections", 67 | "spl", 68 | "eventmanager", 69 | "annotations", 70 | "persistence" 71 | ] 72 | }, 73 | { 74 | "name": "nikic/php-parser", 75 | "version": "dev-master", 76 | "source": { 77 | "type": "git", 78 | "url": "https://github.com/nikic/PHP-Parser", 79 | "reference": "222c9612ab9c65551b7febd38cbb0fa685b0e0db" 80 | }, 81 | "dist": { 82 | "type": "zip", 83 | "url": "https://github.com/nikic/PHP-Parser/archive/222c9612ab9c65551b7febd38cbb0fa685b0e0db.zip", 84 | "reference": "222c9612ab9c65551b7febd38cbb0fa685b0e0db", 85 | "shasum": "" 86 | }, 87 | "require": { 88 | "php": ">=5.2" 89 | }, 90 | "time": "2012-12-21 12:28:35", 91 | "type": "library", 92 | "installation-source": "source", 93 | "autoload": { 94 | "psr-0": { 95 | "PHPParser": "lib/" 96 | } 97 | }, 98 | "notification-url": "https://packagist.org/downloads/", 99 | "license": [ 100 | "BSD-3-Clause" 101 | ], 102 | "authors": [ 103 | { 104 | "name": "Nikita Popov" 105 | } 106 | ], 107 | "description": "A PHP parser written in PHP", 108 | "keywords": [ 109 | "php", 110 | "parser" 111 | ] 112 | }, 113 | { 114 | "name": "symfony/console", 115 | "version": "v2.1.6", 116 | "target-dir": "Symfony/Component/Console", 117 | "source": { 118 | "type": "git", 119 | "url": "https://github.com/symfony/Console", 120 | "reference": "v2.1.6" 121 | }, 122 | "dist": { 123 | "type": "zip", 124 | "url": "https://github.com/symfony/Console/archive/v2.1.6.zip", 125 | "reference": "v2.1.6", 126 | "shasum": "" 127 | }, 128 | "require": { 129 | "php": ">=5.3.3" 130 | }, 131 | "time": "2012-12-13 17:42:00", 132 | "type": "library", 133 | "installation-source": "dist", 134 | "autoload": { 135 | "psr-0": { 136 | "Symfony\\Component\\Console": "" 137 | } 138 | }, 139 | "license": [ 140 | "MIT" 141 | ], 142 | "authors": [ 143 | { 144 | "name": "Fabien Potencier", 145 | "email": "fabien@symfony.com" 146 | }, 147 | { 148 | "name": "Symfony Community", 149 | "homepage": "http://symfony.com/contributors" 150 | } 151 | ], 152 | "description": "Symfony Console Component", 153 | "homepage": "http://symfony.com" 154 | }, 155 | { 156 | "name": "symfony/yaml", 157 | "version": "v2.1.6", 158 | "target-dir": "Symfony/Component/Yaml", 159 | "source": { 160 | "type": "git", 161 | "url": "https://github.com/symfony/Yaml", 162 | "reference": "v2.1.6" 163 | }, 164 | "dist": { 165 | "type": "zip", 166 | "url": "https://github.com/symfony/Yaml/archive/v2.1.6.zip", 167 | "reference": "v2.1.6", 168 | "shasum": "" 169 | }, 170 | "require": { 171 | "php": ">=5.3.3" 172 | }, 173 | "time": "2012-12-06 10:00:55", 174 | "type": "library", 175 | "installation-source": "dist", 176 | "autoload": { 177 | "psr-0": { 178 | "Symfony\\Component\\Yaml": "" 179 | } 180 | }, 181 | "license": [ 182 | "MIT" 183 | ], 184 | "authors": [ 185 | { 186 | "name": "Fabien Potencier", 187 | "email": "fabien@symfony.com" 188 | }, 189 | { 190 | "name": "Symfony Community", 191 | "homepage": "http://symfony.com/contributors" 192 | } 193 | ], 194 | "description": "Symfony Yaml Component", 195 | "homepage": "http://symfony.com" 196 | } 197 | ], 198 | "packages-dev": null, 199 | "aliases": [ 200 | 201 | ], 202 | "minimum-stability": "stable", 203 | "stability-flags": { 204 | "nikic/php-parser": 20 205 | } 206 | } 207 | --------------------------------------------------------------------------------