Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information, see
19
 * <http://www.doctrine-project.org>.
20
 */
21
 
22
/**
23
 * Doctrine_Import_Builder
24
 *
25
 * Import builder is responsible of building Doctrine_Record classes
26
 * based on a database schema.
27
 *
28
 * @package     Doctrine
29
 * @subpackage  Import
30
 * @link        www.doctrine-project.org
31
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
32
 * @since       1.0
33
 * @version     $Revision: 7490 $
34
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
35
 * @author      Jukka Hassinen <Jukka.Hassinen@BrainAlliance.com>
36
 * @author      Nicolas Bérard-Nault <nicobn@php.net>
37
 * @author      Jonathan H. Wage <jwage@mac.com>
38
 */
39
class Doctrine_Import_Builder extends Doctrine_Builder
40
{
41
    /**
42
     * Path where to generated files
43
     *
44
     * @var string $_path
45
     */
46
    protected $_path = '';
47
 
48
    /**
49
     * Class prefix for generated packages
50
     *
51
     * @var string
52
     */
53
    protected $_packagesPrefix = 'Package';
54
 
55
    /**
56
     * Path to generate packages
57
     *
58
     * @var string
59
     */
60
    protected $_packagesPath = '';
61
 
62
    /**
63
     * Name of folder to generate packages in
64
     *
65
     * @var string
66
     */
67
    protected $_packagesFolderName = 'packages';
68
 
69
    /**
70
     * File suffix to use when writing class definitions
71
     *
72
     * @var string $suffix
73
     */
74
    protected $_suffix = '.php';
75
 
76
    /**
77
     * Bool true/false for whether or not to generate base classes
78
     *
79
     * @var boolean $generateBaseClasses
80
     */
81
    protected $_generateBaseClasses = true;
82
 
83
    /**
84
     * Bool true/false for whether or not to generate child table classes
85
     *
86
     * @var boolean $generateTableClasses
87
     */
88
    protected $_generateTableClasses = false;
89
 
90
    /**
91
     * Prefix to use for generated base classes
92
     *
93
     * @var string
94
     */
95
    protected $_baseClassPrefix = 'Base';
96
 
97
    /**
98
     * Directory to put the generate base classes in
99
     *
100
     * @var string $suffix
101
     */
102
    protected $_baseClassesDirectory = 'generated';
103
 
104
    /**
105
     * Base class name for generated classes
106
     *
107
     * @var string
108
     */
109
    protected $_baseClassName = 'Doctrine_Record';
110
 
111
    /**
112
     * Base table class name for generated classes
113
     *
114
     * @var string
115
     */
116
    protected $_baseTableClassName = 'Doctrine_Table';
117
 
118
    /**
119
     * Format to use for generating the model table classes
120
     *
121
     * @var string
122
     */
123
    protected $_tableClassFormat = '%sTable';
124
 
125
    /**
126
     * Prefix to all generated classes
127
     *
128
     * @var string
129
     */
130
    protected $_classPrefix = null;
131
 
132
    /**
133
     * Whether to use the class prefix for the filenames too
134
     *
135
     * @var boolean
136
     **/
137
    protected $_classPrefixFiles = true;
138
 
139
    /**
140
     * Whether or not to generate PEAR style directories and files
141
     *
142
     * @var boolean
143
     */
144
    protected $_pearStyle = false;
145
 
146
    /**
147
     * Allows to force a line-ending style, by default PHP_EOL will be used
148
     *
149
     * @var string
150
     */
151
    protected $_eolStyle = null;
152
 
153
    /**
154
     * The package name to use for the generated php docs
155
     *
156
     * @var string
157
     */
158
    protected $_phpDocPackage = '##PACKAGE##';
159
 
160
    /**
161
     * The subpackage name to use for the generated php docs
162
     *
163
     * @var string
164
     */
165
    protected $_phpDocSubpackage = '##SUBPACKAGE##';
166
 
167
    /**
168
     * Full name of the author to use for the generated php docs
169
     *
170
     * @var string
171
     */
172
    protected $_phpDocName = '##NAME##';
173
 
174
    /**
175
     * Email of the author to use for the generated php docs
176
     *
177
     * @var string
178
     */
179
    protected $_phpDocEmail = '##EMAIL##';
180
 
181
    /**
182
     * _tpl
183
     *
184
     * Class template used for writing classes
185
     *
186
     * @var $_tpl
187
     */
188
    protected static $_tpl;
189
 
190
    /**
191
     * __construct
192
     *
193
     * @return void
194
     */
195
    public function __construct()
196
    {
197
        $manager = Doctrine_Manager::getInstance();
198
        if ($tableClass = $manager->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS)) {
199
            $this->_baseTableClassName = $tableClass;
200
        }
201
        if ($classPrefix = $manager->getAttribute(Doctrine_Core::ATTR_MODEL_CLASS_PREFIX)) {
202
            $this->_classPrefix = $classPrefix;
203
        }
204
        if ($tableClassFormat = $manager->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS_FORMAT)) {
205
            $this->_tableClassFormat = $tableClassFormat;
206
        }
207
        $this->loadTemplate();
208
    }
209
 
210
    /**
211
     * setTargetPath
212
     *
213
     * @param string path   the path where imported files are being generated
214
     * @return
215
     */
216
    public function setTargetPath($path)
217
    {
218
        if ($path) {
219
            if ( ! $this->_packagesPath) {
220
                $this->setOption('packagesPath', $path . DIRECTORY_SEPARATOR . $this->_packagesFolderName);
221
            }
222
 
223
            $this->_path = $path;
224
        }
225
    }
226
 
227
    /**
228
     * generateBaseClasses
229
     *
230
     * Specify whether or not to generate classes which extend from generated base classes
231
     *
232
     * @param  boolean $bool
233
     * @return boolean $bool
234
     */
235
    public function generateBaseClasses($bool = null)
236
    {
237
        if ($bool !== null) {
238
            $this->_generateBaseClasses = $bool;
239
        }
240
 
241
        return $this->_generateBaseClasses;
242
    }
243
 
244
    /**
245
     * generateTableClasses
246
     *
247
     * Specify whether or not to generate children table classes
248
     *
249
     * @param  boolean $bool
250
     * @return boolean $bool
251
     */
252
    public function generateTableClasses($bool = null)
253
    {
254
        if ($bool !== null) {
255
            $this->_generateTableClasses = $bool;
256
        }
257
 
258
        return $this->_generateTableClasses;
259
    }
260
 
261
    /**
262
     * getTargetPath
263
     *
264
     * @return string       the path where imported files are being generated
265
     */
266
    public function getTargetPath()
267
    {
268
        return $this->_path;
269
    }
270
 
271
    /**
272
     * setOptions
273
     *
274
     * @param string $options
275
     * @return void
276
     */
277
    public function setOptions($options)
278
    {
279
        if ( ! empty($options)) {
280
            foreach ($options as $key => $value) {
281
                $this->setOption($key, $value);
282
            }
283
        }
284
    }
285
 
286
    /**
287
     * setOption
288
     *
289
     * @param string $key
290
     * @param string $value
291
     * @return void
292
     */
293
    public function setOption($key, $value)
294
    {
295
        $name = 'set' . Doctrine_Inflector::classify($key);
296
 
297
        if (method_exists($this, $name)) {
298
            $this->$name($value);
299
        } else {
300
            $key = '_' . $key;
301
            $this->$key = $value;
302
        }
303
    }
304
 
305
    /**
306
     * loadTemplate
307
     *
308
     * Loads the class template used for generating classes
309
     *
310
     * @return void
311
     */
312
    public function loadTemplate()
313
    {
314
        if (isset(self::$_tpl)) {
315
            return;
316
        }
317
 
318
        self::$_tpl = '/**'
319
                    . '%s' . PHP_EOL
320
                    . ' */' . PHP_EOL
321
                    . '%sclass %s extends %s' . PHP_EOL
322
                    . '{'
323
                    . '%s' . PHP_EOL
324
                    . '%s' . PHP_EOL
325
                    . '}';
326
    }
327
 
328
    /*
329
     * Build the table definition of a Doctrine_Record object
330
     *
331
     * @param  string $table
332
     * @param  array  $tableColumns
333
     */
334
    public function buildTableDefinition(array $definition)
335
    {
336
        if (isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
337
            return;
338
        }
339
 
340
        $ret = array();
341
 
342
        $i = 0;
343
 
344
        if (isset($definition['inheritance']['type']) && $definition['inheritance']['type'] == 'concrete') {
345
            $ret[$i] = "        parent::setTableDefinition();";
346
            $i++;
347
        }
348
 
349
        if (isset($definition['tableName']) && !empty($definition['tableName'])) {
350
            $ret[$i] = "        ".'$this->setTableName(\''. $definition['tableName'].'\');';
351
            $i++;
352
        }
353
 
354
        if (isset($definition['columns']) && is_array($definition['columns']) && !empty($definition['columns'])) {
355
            $ret[$i] = $this->buildColumns($definition['columns']);
356
            $i++;
357
        }
358
 
359
        if (isset($definition['indexes']) && is_array($definition['indexes']) && !empty($definition['indexes'])) {
360
            $ret[$i] = $this->buildIndexes($definition['indexes']);
361
            $i++;
362
        }
363
 
364
        if (isset($definition['attributes']) && is_array($definition['attributes']) && !empty($definition['attributes'])) {
365
            $ret[$i] = $this->buildAttributes($definition['attributes']);
366
            $i++;
367
        }
368
 
369
        if (isset($definition['options']) && is_array($definition['options']) && !empty($definition['options'])) {
370
            $ret[$i] = $this->buildOptions($definition['options']);
371
            $i++;
372
        }
373
 
374
        if (isset($definition['checks']) && is_array($definition['checks']) && !empty($definition['checks'])) {
375
            $ret[$i] = $this->buildChecks($definition['checks']);
376
            $i++;
377
        }
378
 
379
        if (isset($definition['inheritance']['subclasses']) && ! empty($definition['inheritance']['subclasses'])) {
380
            $subClasses = array();
381
            foreach ($definition['inheritance']['subclasses'] as $className => $def) {
382
                $className = $this->_classPrefix . $className;
383
                $subClasses[$className] = $def;
384
            }
385
            $ret[$i] = "        ".'$this->setSubClasses('. $this->varExport($subClasses).');';
386
            $i++;
387
        }
388
 
389
        $code = implode(PHP_EOL, $ret);
390
        $code = trim($code);
391
 
392
        return PHP_EOL . "    public function setTableDefinition()" . PHP_EOL . '    {' . PHP_EOL . '        ' . $code . PHP_EOL . '    }';
393
    }
394
 
395
    /**
396
     * buildSetUp
397
     *
398
     * @param  array $options
399
     * @param  array $columns
400
     * @param  array $relations
401
     * @return string
402
     */
403
    public function buildSetUp(array $definition)
404
    {
405
        $ret = array();
406
        $i = 0;
407
 
408
        if (isset($definition['relations']) && is_array($definition['relations']) && ! empty($definition['relations'])) {
409
            foreach ($definition['relations'] as $name => $relation) {
410
                $class = isset($relation['class']) ? $relation['class']:$name;
411
                $alias = (isset($relation['alias']) && $relation['alias'] !== $this->_classPrefix . $relation['class']) ? ' as ' . $relation['alias'] : '';
412
 
413
                if ( ! isset($relation['type'])) {
414
                    $relation['type'] = Doctrine_Relation::ONE;
415
                }
416
 
417
                if ($relation['type'] === Doctrine_Relation::ONE) {
418
                    $ret[$i] = "        ".'$this->hasOne(\'' . $class . $alias . '\'';
419
                } else {
420
                    $ret[$i] = "        ".'$this->hasMany(\'' . $class . $alias . '\'';
421
                }
422
 
423
                $a = array();
424
 
425
                if (isset($relation['refClass'])) {
426
                    $a[] = '\'refClass\' => ' . $this->varExport($relation['refClass']);
427
                }
428
 
429
                if (isset($relation['refClassRelationAlias'])) {
430
                    $a[] = '\'refClassRelationAlias\' => ' . $this->varExport($relation['refClassRelationAlias']);
431
                }
432
 
433
                if (isset($relation['deferred']) && $relation['deferred']) {
434
                    $a[] = '\'default\' => ' . $this->varExport($relation['deferred']);
435
                }
436
 
437
                if (isset($relation['local']) && $relation['local']) {
438
                    $a[] = '\'local\' => ' . $this->varExport($relation['local']);
439
                }
440
 
441
                if (isset($relation['foreign']) && $relation['foreign']) {
442
                    $a[] = '\'foreign\' => ' . $this->varExport($relation['foreign']);
443
                }
444
 
445
                if (isset($relation['onDelete']) && $relation['onDelete']) {
446
                    $a[] = '\'onDelete\' => ' . $this->varExport($relation['onDelete']);
447
                }
448
 
449
                if (isset($relation['onUpdate']) && $relation['onUpdate']) {
450
                    $a[] = '\'onUpdate\' => ' . $this->varExport($relation['onUpdate']);
451
                }
452
 
453
                if (isset($relation['cascade']) && $relation['cascade']) {
454
                    $a[] = '\'cascade\' => ' . $this->varExport($relation['cascade']);
455
                }
456
 
457
                if (isset($relation['equal']) && $relation['equal']) {
458
                    $a[] = '\'equal\' => ' . $this->varExport($relation['equal']);
459
                }
460
 
461
                if (isset($relation['owningSide']) && $relation['owningSide']) {
462
                    $a[] = '\'owningSide\' => ' . $this->varExport($relation['owningSide']);
463
                }
464
 
465
                if (isset($relation['foreignKeyName']) && $relation['foreignKeyName']) {
466
                    $a[] = '\'foreignKeyName\' => ' . $this->varExport($relation['foreignKeyName']);
467
                }
468
 
469
                if (isset($relation['orderBy']) && $relation['orderBy']) {
470
                    $a[] = '\'orderBy\' => ' . $this->varExport($relation['orderBy']);
471
                }
472
 
473
                if ( ! empty($a)) {
474
                    $ret[$i] .= ', ' . 'array(' . PHP_EOL . str_repeat(' ', 13);
475
                    $length = strlen($ret[$i]);
476
                    $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', 13), $a) . ')';
477
                }
478
 
479
                $ret[$i] .= ');'.PHP_EOL;
480
                $i++;
481
            }
482
        }
483
 
484
        if (isset($definition['actAs']) && is_array($definition['actAs']) && !empty($definition['actAs'])) {
485
            $ret[$i] = $this->buildActAs($definition['actAs']);
486
            $i++;
487
        }
488
 
489
        if (isset($definition['listeners']) && is_array($definition['listeners']) && !empty($definition['listeners'])) {
490
            $ret[$i] = $this->buildListeners($definition['listeners']);
491
            $i++;
492
        }
493
 
494
        $code = implode(PHP_EOL, $ret);
495
        $code = trim($code);
496
 
497
        $code = "parent::setUp();" . PHP_EOL . '        ' . $code;
498
 
499
        // If we have some code for the function then lets define it and return it
500
        if ($code) {
501
            return '    public function setUp()' . PHP_EOL . '    {' . PHP_EOL . '        ' . $code . PHP_EOL . '    }';
502
        }
503
    }
504
 
505
    /**
506
     * Build php code for record checks
507
     *
508
     * @param array $checks
509
     * @return string $build
510
     */
511
    public function buildChecks($checks)
512
    {
513
        $build = '';
514
        foreach ($checks as $check) {
515
            $build .= "        \$this->check('" . $check . "');" . PHP_EOL;
516
        }
517
        return $build;
518
    }
519
 
520
    /**
521
     * buildColumns
522
     *
523
     * @param string $array
524
     * @return void
525
     */
526
    public function buildColumns(array $columns)
527
    {
528
        $manager = Doctrine_Manager::getInstance();
529
        $refl = new ReflectionClass($this->_baseClassName);
530
 
531
        $build = null;
532
        foreach ($columns as $name => $column) {
533
            // An alias cannot passed via column name and column alias definition
534
            if (isset($column['name']) && stripos($column['name'], ' as ') && isset($column['alias'])) {
535
                throw new Doctrine_Import_Exception(
536
                    sprintf('When using a column alias you cannot pass it via column name and column alias definition (column: %s).', $column['name'])
537
                );
538
            }
539
 
540
            // Update column name if an alias is provided
541
            if (isset($column['alias']) && !isset($column['name'])) {
542
                $column['name'] = $name . ' as ' . $column['alias'];
543
            }
544
 
545
            $columnName = isset($column['name']) ? $column['name']:$name;
546
            if ($manager->getAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE)) {
547
                $e = explode(' as ', $columnName);
548
                $fieldName = isset($e[1]) ? $e[1] : $e[0];
549
                $classified = Doctrine_Inflector::classify($fieldName);
550
                $getter = 'get' . $classified;
551
                $setter = 'set' . $classified;
552
 
553
                if ($refl->hasMethod($getter) || $refl->hasMethod($setter)) {
554
                    throw new Doctrine_Import_Exception(
555
                        sprintf('When using the attribute ATTR_AUTO_ACCESSOR_OVERRIDE you cannot use the field name "%s" because it is reserved by Doctrine. You must choose another field name.', $fieldName)
556
                    );
557
                }
558
            }
559
            $build .= "        ".'$this->hasColumn(\'' . $columnName . '\', \'' . $column['type'] . '\'';
560
 
561
            if ($column['length']) {
562
                $build .= ', ' . $column['length'];
563
            } else {
564
                $build .= ', null';
565
            }
566
 
567
            $options = $column;
568
 
569
            // Remove name, alltypes, ntype. They are not needed in options array
570
            unset($options['name']);
571
            unset($options['alltypes']);
572
            unset($options['ntype']);
573
 
574
            // Remove notnull => true if the column is primary
575
            // Primary columns are implied to be notnull in Doctrine
576
            if (isset($options['primary']) && $options['primary'] == true && (isset($options['notnull']) && $options['notnull'] == true)) {
577
                unset($options['notnull']);
578
            }
579
 
580
            // Remove default if the value is 0 and the column is a primary key
581
            // Doctrine defaults to 0 if it is a primary key
582
            if (isset($options['primary']) && $options['primary'] == true && (isset($options['default']) && $options['default'] == 0)) {
583
                unset($options['default']);
584
            }
585
 
586
            // Remove null and empty array values
587
            foreach ($options as $key => $value) {
588
                if (is_null($value) || (is_array($value) && empty($value))) {
589
                    unset($options[$key]);
590
                }
591
            }
592
 
593
            if (is_array($options) && !empty($options)) {
594
                $build .= ', ' . $this->varExport($options);
595
            }
596
 
597
            $build .= ');' . PHP_EOL;
598
        }
599
 
600
        return $build;
601
    }
602
 
603
    /*
604
     * Build the accessors
605
     *
606
     * @param  string $table
607
     * @param  array  $columns
608
     */
609
    public function buildAccessors(array $definition)
610
    {
611
        $accessors = array();
612
        foreach (array_keys($definition['columns']) as $name) {
613
            $accessors[] = $name;
614
        }
615
 
616
        foreach ($definition['relations'] as $relation) {
617
            $accessors[] = $relation['alias'];
618
        }
619
 
620
        $ret = '';
621
        foreach ($accessors as $name) {
622
            // getters
623
            $ret .= PHP_EOL . '  public function get' . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\$load = true)" . PHP_EOL;
624
            $ret .= "  {" . PHP_EOL;
625
            $ret .= "    return \$this->get('{$name}', \$load);" . PHP_EOL;
626
            $ret .= "  }" . PHP_EOL;
627
 
628
            // setters
629
            $ret .= PHP_EOL . '  public function set' . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\${$name}, \$load = true)" . PHP_EOL;
630
            $ret .= "  {" . PHP_EOL;
631
            $ret .= "    return \$this->set('{$name}', \${$name}, \$load);" . PHP_EOL;
632
            $ret .= "  }" . PHP_EOL;
633
        }
634
 
635
        return $ret;
636
    }
637
 
638
    /*
639
     * Build the phpDoc for a class definition
640
     *
641
     * @param  array  $definition
642
     */
643
    public function buildPhpDocs(array $definition)
644
    {
645
        $ret = array();
646
 
647
        $ret[] = $definition['className'];
648
        $ret[] = '';
649
        $ret[] = 'This class has been auto-generated by the Doctrine ORM Framework';
650
        $ret[] = '';
651
 
652
        if ((isset($definition['is_base_class']) && $definition['is_base_class']) || ! $this->generateBaseClasses()) {
653
            foreach ($definition['columns'] as $name => $column) {
654
                $name = isset($column['name']) ? $column['name']:$name;
655
                // extract column name & field name
656
                if (stripos($name, ' as '))
657
                {
658
                    if (strpos($name, ' as')) {
659
                        $parts = explode(' as ', $name);
660
                    } else {
661
                        $parts = explode(' AS ', $name);
662
                    }
663
 
664
                    if (count($parts) > 1) {
665
                        $fieldName = $parts[1];
666
                    } else {
667
                        $fieldName = $parts[0];
668
                    }
669
 
670
                    $name = $parts[0];
671
                } else {
672
                    $fieldName = $name;
673
                    $name = $name;
674
                }
675
 
676
                $name = trim($name);
677
                $fieldName = trim($fieldName);
678
 
679
                $ret[] = '@property ' . $column['type'] . ' $' . $fieldName;
680
            }
681
 
682
            if (isset($definition['relations']) && ! empty($definition['relations'])) {
683
                foreach ($definition['relations'] as $relation) {
684
                    $type = (isset($relation['type']) && $relation['type'] == Doctrine_Relation::MANY) ? 'Doctrine_Collection' : $this->_classPrefix . $relation['class'];
685
                    $ret[] = '@property ' . $type . ' $' . $relation['alias'];
686
                }
687
            }
688
            $ret[] = '';
689
        }
690
 
691
        $ret[] = '@package    ' . $this->_phpDocPackage;
692
        $ret[] = '@subpackage ' . $this->_phpDocSubpackage;
693
        $ret[] = '@author     ' . $this->_phpDocName . ' <' . $this->_phpDocEmail . '>';
694
        $ret[] = '@version    SVN: $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $';
695
 
696
        $ret = ' * ' . implode(PHP_EOL . ' * ', $ret);
697
        $ret = ' ' . trim($ret);
698
 
699
        return $ret;
700
    }
701
 
702
    /**
703
     * emit a behavior assign
704
     *
705
     * @param int $level
706
     * @param string $name
707
     * @param string $option
708
     * @return string assignation code
709
     */
710
    private function emitAssign($level, $name, $option)
711
    {
712
        // find class matching $name
713
        $classname = $name;
714
        if (class_exists("Doctrine_Template_$name", true)) {
715
            $classname = "Doctrine_Template_$name";
716
        }
717
        return "        \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL;
718
    }
719
 
720
    /**
721
     * emit an addChild
722
     *
723
     * @param int $level
724
     * @param string $name
725
     * @param string $option
726
     * @return string addChild code
727
     */
728
    private function emitAddChild($level, $parent, $name)
729
    {
730
        return "        \$" . strtolower($parent) . ($level - 1) . "->addChild(\$" . strtolower($name) . "$level);" . PHP_EOL;
731
    }
732
 
733
    /**
734
     * emit an indented actAs
735
     *
736
     * @param int $level
737
     * @param string $name
738
     * @param string $option
739
     * @return string actAs code
740
     */
741
    private function emitActAs($level, $name)
742
    {
743
        return "        \$this->actAs(\$" . strtolower($name) . "$level);" . PHP_EOL;
744
    }
745
 
746
    /**
747
     * buildActAs: builds a complete actAs code. It supports hierarchy of plugins
748
     * @param array $actAs array of plugin definitions and options
749
     */
750
    public function buildActAs($actAs)
751
    {
752
        $emittedActAs = array();
753
        $build = $this->innerBuildActAs($actAs, 0, null, $emittedActAs);
754
        foreach($emittedActAs as $str) {
755
            $build .= $str;
756
        }
757
        return $build;
758
    }
759
 
760
    /**
761
     * innerBuildActAs: build a complete actAs code that handles hierarchy of plugins
762
     *
763
     * @param array  $actAs array of plugin definitions and options
764
     * @param int    $level current indentation level
765
     * @param string $parent name of the parent template/plugin
766
     * @param array  $emittedActAs contains on output an array of actAs command to be appended to output
767
     * @return string actAs full definition
768
     */
769
    private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emittedActAs)
770
    {
771
        // rewrite special case of actAs: [Behavior] which gave [0] => Behavior
772
        if (is_array($actAs) && isset($actAs[0]) && !is_array($actAs[0])) {
773
            $tmp = array();
774
            foreach ($actAs as $key => $value) {
775
                if (is_numeric($key)) {
776
                    $tmp[(string)$value] = null;
777
                } else {
778
                    $tmp[$key] = $value;
779
                }
780
            }
781
            $actAs = $tmp;
782
        }
783
 
784
        $build = '';
785
        $currentParent = $parent;
786
        if (is_array($actAs)) {
787
            foreach($actAs as $template => $options) {
788
                if ($template == 'actAs') {
789
                    // found another actAs
790
                    $build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs);
791
                } else if (is_array($options)) {
792
                    // remove actAs from options
793
                    $realOptions = array();
794
                    $leftActAs = array();
795
                    foreach($options as $name => $value) {
796
                        if ($name != 'actAs') {
797
                            $realOptions[$name] = $options[$name];
798
                        } else {
799
                            $leftActAs[$name] = $options[$name];
800
                        }
801
                    }
802
 
803
                    $optionPHP = $this->varExport($realOptions);
804
                    $build .= $this->emitAssign($level, $template, $optionPHP);
805
                    if ($level == 0) {
806
                        $emittedActAs[] = $this->emitActAs($level, $template);
807
                    } else {
808
                        $build .= $this->emitAddChild($level, $currentParent, $template);
809
                    }
810
                    // descend for the remainings actAs
811
                    $parent = $template;
812
                    $build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs);
813
                } else {
814
                    $build .= $this->emitAssign($level, $template, null);
815
                    if ($level == 0) {
816
                        $emittedActAs[] = $this->emitActAs($level, $template);
817
                    } else {
818
                        $build .= $this->emitAddChild($level, $currentParent, $template);
819
                    }
820
                    $parent = $template;
821
                }
822
            }
823
        } else {
824
            $build .= $this->emitAssign($level, $actAs, null);
825
            if ($level == 0) {
826
                $emittedActAs[] = $this->emitActAs($level, $actAs);
827
            } else {
828
                $build .= $this->emitAddChild($level, $currentParent, $actAs);
829
            }
830
        }
831
 
832
        return $build;
833
    }
834
 
835
    /**
836
     * Build php code for adding record listeners
837
     *
838
     * @param string $listeners
839
     * @return string $build
840
     */
841
    public function buildListeners($listeners)
842
    {
843
        $build = '';
844
 
845
        foreach($listeners as $name => $options) {
846
            if ( ! is_array($options) && $options !== null) {
847
                $name = $options;
848
                $options = null;
849
            }
850
 
851
            $useOptions = ( ! empty($options) && isset($options['useOptions']) && $options['useOptions'] == true)
852
                ? '$this->getTable()->getOptions()' : 'array()';
853
            $class = ( ! empty($options) && isset($options['class'])) ? $options['class'] : $name;
854
 
855
            $build .= "    \$this->addListener(new " . $class . "(" . $useOptions . "), '" . $name . "');" . PHP_EOL;
856
        }
857
 
858
        return $build;
859
    }
860
 
861
    /**
862
     * buildAttributes
863
     *
864
     * @param string $array
865
     * @return void
866
     */
867
    public function buildAttributes(array $attributes)
868
    {
869
        $build = PHP_EOL;
870
        foreach ($attributes as $key => $value) {
871
 
872
            $values = array();
873
            if (is_bool($value))
874
            {
875
              $values[] = $value ? 'true':'false';
876
            } else {
877
                if ( ! is_array($value)) {
878
                    $value = array($value);
879
                }
880
 
881
                foreach ($value as $attr) {
882
                    $const = "Doctrine_Core::" . strtoupper($key) . "_" . strtoupper($attr);
883
                    if (defined($const)) {
884
                        $values[] = $const;
885
                    } else {
886
                        $values[] = "'" . $attr . "'";
887
                    }
888
                }
889
            }
890
 
891
            $string = implode(' ^ ', $values);
892
            $build .= "        \$this->setAttribute(Doctrine_Core::ATTR_" . strtoupper($key) . ", " . $string . ");" . PHP_EOL;
893
        }
894
 
895
        return $build;
896
    }
897
 
898
    /**
899
     * buildTableOptions
900
     *
901
     * @param string $array
902
     * @return void
903
     */
904
    public function buildOptions(array $options)
905
    {
906
        $build = '';
907
        foreach ($options as $name => $value) {
908
            $build .= "        \$this->option('$name', " . $this->varExport($value) . ");" . PHP_EOL;
909
        }
910
 
911
        return $build;
912
    }
913
 
914
    /**
915
     * buildIndexes
916
     *
917
     * @param string $array
918
     * @return void
919
     */
920
    public function buildIndexes(array $indexes)
921
    {
922
      $build = '';
923
 
924
      foreach ($indexes as $indexName => $definitions) {
925
          $build .= PHP_EOL . "        \$this->index('" . $indexName . "'";
926
          $build .= ', ' . $this->varExport($definitions);
927
          $build .= ');';
928
      }
929
 
930
      return $build;
931
    }
932
 
933
    /**
934
     * buildToString
935
     *
936
     * @param array $definition
937
     * @return string
938
     */
939
    public function buildToString(array $definition)
940
    {
941
        if ( empty($definition['toString'])) {
942
            return '';
943
        }
944
 
945
        $ret = PHP_EOL . PHP_EOL . '    public function __toString()' . PHP_EOL;
946
        $ret .= "    {" . PHP_EOL;
947
        $ret .= "      return (string) \$this->".$definition['toString'].";" . PHP_EOL;
948
        $ret .= "    }";
949
        return $ret;
950
    }
951
 
952
    /**
953
     * buildDefinition
954
     *
955
     * @param array $definition
956
     * @return string
957
     */
958
    public function buildDefinition(array $definition)
959
    {
960
        if ( ! isset($definition['className'])) {
961
            throw new Doctrine_Import_Builder_Exception('Missing class name.');
962
        }
963
        $abstract = isset($definition['abstract']) && $definition['abstract'] === true ? 'abstract ':null;
964
        $className = $definition['className'];
965
        $extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName;
966
 
967
        if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) {
968
            $tableDefinitionCode = $this->buildTableDefinition($definition);
969
            $setUpCode = $this->buildSetUp($definition);
970
        } else {
971
            $tableDefinitionCode = null;
972
            $setUpCode = null;
973
        }
974
 
975
        if ($tableDefinitionCode && $setUpCode) {
976
            $setUpCode = PHP_EOL . $setUpCode;
977
        }
978
 
979
        $setUpCode.= $this->buildToString($definition);
980
 
981
        $docs = PHP_EOL . $this->buildPhpDocs($definition);
982
 
983
        $content = sprintf(self::$_tpl, $docs, $abstract,
984
                                       $className,
985
                                       $extends,
986
                                       $tableDefinitionCode,
987
                                       $setUpCode);
988
 
989
        return $content;
990
    }
991
 
992
    /**
993
     * buildRecord
994
     *
995
     * @param array $options
996
     * @param array $columns
997
     * @param array $relations
998
     * @param array $indexes
999
     * @param array $attributes
1000
     * @param array $templates
1001
     * @param array $actAs
1002
     * @return void=
1003
     */
1004
    public function buildRecord(array $definition)
1005
    {
1006
        if ( ! isset($definition['className'])) {
1007
            throw new Doctrine_Import_Builder_Exception('Missing class name.');
1008
        }
1009
 
1010
        $definition['topLevelClassName'] = $definition['className'];
1011
 
1012
        if ($this->generateBaseClasses()) {
1013
            $definition['is_package'] = (isset($definition['package']) && $definition['package']) ? true:false;
1014
 
1015
            if ($definition['is_package']) {
1016
                $e = explode('.', trim($definition['package']));
1017
                $definition['package_name'] = $e[0];
1018
 
1019
                $definition['package_path'] = ! empty($e) ? implode(DIRECTORY_SEPARATOR, $e):$definition['package_name'];
1020
            }
1021
            // Top level definition that extends from all the others
1022
            $topLevel = $definition;
1023
            unset($topLevel['tableName']);
1024
 
1025
            // If we have a package then we need to make this extend the package definition and not the base definition
1026
            // The package definition will then extends the base definition
1027
            $topLevel['inheritance']['extends'] = (isset($topLevel['package']) && $topLevel['package']) ? $this->_packagesPrefix . $topLevel['className']:$this->_baseClassPrefix . $topLevel['className'];
1028
            $topLevel['no_definition'] = true;
1029
            $topLevel['generate_once'] = true;
1030
            $topLevel['is_main_class'] = true;
1031
            unset($topLevel['connection']);
1032
 
1033
            // Package level definition that extends from the base definition
1034
            if (isset($definition['package'])) {
1035
 
1036
                $packageLevel = $definition;
1037
                $packageLevel['className'] = $topLevel['inheritance']['extends'];
1038
                $packageLevel['inheritance']['extends'] = $this->_baseClassPrefix . $topLevel['className'];
1039
                $packageLevel['no_definition'] = true;
1040
                $packageLevel['abstract'] = true;
1041
                $packageLevel['override_parent'] = true;
1042
                $packageLevel['generate_once'] = true;
1043
                $packageLevel['is_package_class'] = true;
1044
                unset($packageLevel['connection']);
1045
 
1046
                $packageLevel['tableClassName'] = sprintf($this->_tableClassFormat, $packageLevel['className']);
1047
                $packageLevel['inheritance']['tableExtends'] = isset($definition['inheritance']['extends']) ? sprintf($this->_tableClassFormat, $definition['inheritance']['extends']):$this->_baseTableClassName;
1048
 
1049
                $topLevel['tableClassName'] = sprintf($this->_tableClassFormat, $topLevel['topLevelClassName']);
1050
                $topLevel['inheritance']['tableExtends'] = sprintf($this->_tableClassFormat, $packageLevel['className']);
1051
            } else {
1052
                $topLevel['tableClassName'] = sprintf($this->_tableClassFormat, $topLevel['className']);
1053
                $topLevel['inheritance']['tableExtends'] = isset($definition['inheritance']['extends']) ? sprintf($this->_tableClassFormat, $definition['inheritance']['extends']):$this->_baseTableClassName;
1054
            }
1055
 
1056
            $baseClass = $definition;
1057
            $baseClass['className'] = $this->_getBaseClassName($baseClass['className']);
1058
            $baseClass['abstract'] = true;
1059
            $baseClass['override_parent'] = false;
1060
            $baseClass['is_base_class'] = true;
1061
 
1062
            $this->writeDefinition($baseClass);
1063
 
1064
            if ( ! empty($packageLevel)) {
1065
                $this->writeDefinition($packageLevel);
1066
            }
1067
 
1068
            $this->writeDefinition($topLevel);
1069
        } else {
1070
            $this->writeDefinition($definition);
1071
        }
1072
    }
1073
 
1074
    protected function _getBaseClassName($className)
1075
    {
1076
        return $this->_baseClassPrefix . $className;
1077
    }
1078
 
1079
    public function buildTableClassDefinition($className, $definition, $options = array())
1080
    {
1081
        $extends = isset($options['extends']) ? $options['extends']:$this->_baseTableClassName;
1082
        if ($extends !== $this->_baseTableClassName) {
1083
            $extends = $this->_classPrefix . $extends;
1084
        }
1085
 
1086
        $code = sprintf("    /**
1087
     * Returns an instance of this class.
1088
     *
1089
     * @return object %s
1090
     */
1091
    public static function getInstance()
1092
    {
1093
        return Doctrine_Core::getTable('%s');
1094
    }", $className, $definition['className']);
1095
 
1096
        $docBlock = array();
1097
        $docBlock[] = $className;
1098
        $docBlock[] = '';
1099
        $docBlock[] = 'This class has been auto-generated by the Doctrine ORM Framework';
1100
        $docBlock = PHP_EOL.' * ' . implode(PHP_EOL . ' * ', $docBlock);
1101
 
1102
        $content  = '<?php' . PHP_EOL.PHP_EOL;
1103
        $content .= sprintf(self::$_tpl,
1104
            $docBlock,
1105
            false,
1106
            $className,
1107
            $extends,
1108
            null,
1109
            $code,
1110
            null
1111
        );
1112
 
1113
        if ($this->_eolStyle) {
1114
            $content = str_replace(PHP_EOL, $this->_eolStyle, $content);
1115
        }
1116
 
1117
        return $content;
1118
    }
1119
 
1120
    /**
1121
     * writeTableClassDefinition
1122
     *
1123
     * @return void
1124
     */
1125
    public function writeTableClassDefinition(array $definition, $path, $options = array())
1126
    {
1127
        if ($prefix = $this->_classPrefix) {
1128
            $className = $prefix . $definition['tableClassName'];
1129
            if ($this->_classPrefixFiles) {
1130
                $fileName = $className . $this->_suffix;
1131
            } else {
1132
                $fileName = $definition['tableClassName'] . $this->_suffix;
1133
            }
1134
            $writePath = $path . DIRECTORY_SEPARATOR . $fileName;
1135
        } else {
1136
            $className = $definition['tableClassName'];
1137
            $fileName = $className . $this->_suffix;
1138
        }
1139
 
1140
        if ($this->_pearStyle) {
1141
            $writePath = $path . DIRECTORY_SEPARATOR . str_replace('_', '/', $fileName);
1142
        } else {
1143
            $writePath = $path . DIRECTORY_SEPARATOR . $fileName;
1144
        }
1145
 
1146
        $content = $this->buildTableClassDefinition($className, $definition, $options);
1147
 
1148
        Doctrine_Lib::makeDirectories(dirname($writePath));
1149
 
1150
        Doctrine_Core::loadModel($className, $writePath);
1151
 
1152
        if ( ! file_exists($writePath)) {
1153
            file_put_contents($writePath, $content);
1154
        }
1155
    }
1156
 
1157
    /**
1158
     * Return the file name of the class to be generated.
1159
     *
1160
     * @param string $originalClassName
1161
     * @param array $definition
1162
     * @return string
1163
     */
1164
    protected function _getFileName($originalClassName, $definition)
1165
    {
1166
        if ($this->_classPrefixFiles) {
1167
            $fileName = $definition['className'] . $this->_suffix;
1168
        } else {
1169
            $fileName = $originalClassName . $this->_suffix;
1170
        }
1171
 
1172
        if ($this->_pearStyle) {
1173
            $fileName = str_replace('_', '/', $fileName);
1174
        }
1175
 
1176
        return $fileName;
1177
    }
1178
 
1179
    /**
1180
     * writeDefinition
1181
     *
1182
     * @param array $options
1183
     * @param array $columns
1184
     * @param array $relations
1185
     * @param array $indexes
1186
     * @param array $attributes
1187
     * @param array $templates
1188
     * @param array $actAs
1189
     * @return void
1190
     */
1191
    public function writeDefinition(array $definition)
1192
    {
1193
        $originalClassName = $definition['className'];
1194
        if ($prefix = $this->_classPrefix) {
1195
            $definition['className'] = $prefix . $definition['className'];
1196
            if (isset($definition['connectionClassName'])) {
1197
                $definition['connectionClassName'] = $prefix . $definition['connectionClassName'];
1198
            }
1199
            $definition['topLevelClassName'] = $prefix . $definition['topLevelClassName'];
1200
            if (isset($definition['inheritance']['extends'])) {
1201
                $definition['inheritance']['extends'] = $prefix . $definition['inheritance']['extends'];
1202
            }
1203
        }
1204
 
1205
        $definitionCode = $this->buildDefinition($definition);
1206
 
1207
        if ($prefix) {
1208
            $definitionCode = str_replace("this->hasOne('", "this->hasOne('$prefix", $definitionCode);
1209
            $definitionCode = str_replace("this->hasMany('", "this->hasMany('$prefix", $definitionCode);
1210
            $definitionCode = str_replace("'refClass' => '", "'refClass' => '$prefix", $definitionCode);
1211
        }
1212
 
1213
        $fileName = $this->_getFileName($originalClassName, $definition);
1214
 
1215
        $packagesPath = $this->_packagesPath ? $this->_packagesPath:$this->_path;
1216
 
1217
        // If this is a main class that either extends from Base or Package class
1218
        if (isset($definition['is_main_class']) && $definition['is_main_class']) {
1219
            // If is package then we need to put it in a package subfolder
1220
            if (isset($definition['is_package']) && $definition['is_package']) {
1221
                $writePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name'];
1222
            // Otherwise lets just put it in the root of the path
1223
            } else {
1224
                $writePath = $this->_path;
1225
            }
1226
 
1227
            if ($this->generateTableClasses()) {
1228
                $this->writeTableClassDefinition($definition, $writePath, array('extends' => $definition['inheritance']['tableExtends']));
1229
            }
1230
        }
1231
        // If is the package class then we need to make the path to the complete package
1232
        else if (isset($definition['is_package_class']) && $definition['is_package_class']) {
1233
            if (isset($definition['package_custom_path'])) {
1234
              $writePath = $definition['package_custom_path'];
1235
            } else {
1236
              $writePath = $packagesPath . DIRECTORY_SEPARATOR . $definition['package_path'];
1237
            }
1238
 
1239
            if ($this->generateTableClasses()) {
1240
                $this->writeTableClassDefinition($definition, $writePath, array('extends' => $definition['inheritance']['tableExtends']));
1241
            }
1242
        }
1243
        // If it is the base class of the doctrine record definition
1244
        else if (isset($definition['is_base_class']) && $definition['is_base_class']) {
1245
            // If it is a part of a package then we need to put it in a package subfolder
1246
            if (isset($definition['is_package']) && $definition['is_package']) {
1247
                $basePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name'];
1248
                $writePath = $basePath . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
1249
            // Otherwise lets just put it in the root generated folder
1250
            } else {
1251
                $writePath = $this->_path . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
1252
            }
1253
        }
1254
 
1255
        // If we have a writePath from the if else conditionals above then use it
1256
        if (isset($writePath)) {
1257
            Doctrine_Lib::makeDirectories($writePath);
1258
 
1259
            $writePath .= DIRECTORY_SEPARATOR . $fileName;
1260
        // Otherwise none of the conditions were met and we aren't generating base classes
1261
        } else {
1262
            Doctrine_Lib::makeDirectories($this->_path);
1263
 
1264
            $writePath = $this->_path . DIRECTORY_SEPARATOR . $fileName;
1265
        }
1266
 
1267
        $code = "<?php" . PHP_EOL;
1268
 
1269
        if (isset($definition['connection']) && $definition['connection']) {
1270
            $code .= "// Connection Component Binding" . PHP_EOL;
1271
            $code .= "Doctrine_Manager::getInstance()->bindComponent('" . $definition['connectionClassName'] . "', '" . $definition['connection'] . "');" . PHP_EOL;
1272
        }
1273
 
1274
        $code .= PHP_EOL . $definitionCode;
1275
 
1276
        if ($this->_eolStyle) {
1277
            $code = str_replace(PHP_EOL, $this->_eolStyle, $code);
1278
        }
1279
 
1280
        Doctrine_Lib::makeDirectories(dirname($writePath));
1281
 
1282
        if (isset($definition['generate_once']) && $definition['generate_once'] === true) {
1283
            if ( ! file_exists($writePath)) {
1284
                $bytes = file_put_contents($writePath, $code);
1285
            }
1286
        } else {
1287
            $bytes = file_put_contents($writePath, $code);
1288
        }
1289
 
1290
        if (isset($bytes) && $bytes === false) {
1291
            throw new Doctrine_Import_Builder_Exception("Couldn't write file " . $writePath);
1292
        }
1293
 
1294
        Doctrine_Core::loadModel($definition['className'], $writePath);
1295
    }
1296
}