├── .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 |
--------------------------------------------------------------------------------