<?php /* * $Id$ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see * <http://www.doctrine-project.org>. */ namespace Doctrine\ORM\Tools\Export\Driver; use Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\AssociationMapping; /** * ClassMetadata exporter for PHP classes with annotations * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org * @since 2.0 * @version $Revision$ * @author Jonathan Wage <jonwage@gmail.com> */ class AnnotationExporter extends AbstractExporter { protected $_extension = '.php'; private $_isNew = false; private $_outputPath; private $_numSpaces; private $_spaces; private $_classToExtend; private $_currentCode; public function __construct($dir = null) { parent::__construct($dir); $this->setNumSpaces(4); } /** * Converts a single ClassMetadata instance to the exported format * and returns it * * @param ClassMetadataInfo $metadata * @return string $exported */ public function exportClassMetadata(ClassMetadataInfo $metadata) { if (file_exists($this->_outputPath)) { $this->_currentCode = file_get_contents($this->_outputPath); } ob_start(); include($this->_isNew ? 'annotation.tpl.php' : 'annotation_body.tpl.php'); $code = ob_get_contents(); ob_end_clean(); $code = str_replace(array('[?php', '?]'), array('<?php', '?>'), $code); $code = explode("\n", $code); if ($this->_currentCode) { $body = $code; $code = $this->_currentCode; $code = explode("\n", $code); unset($code[array_search('}', $code)]); foreach ($body as $line) { $code[] = $line; } $code[] = '}'; } $code = array_values($code); // Remove empty lines before last "}" for ($i = count($code) - 1; $i > 0; --$i) { $line = trim($code[$i]); if ($line && $line != '}') { break; } if ( ! $line) { unset($code[$i]); } } $code = array_values($code); $exported = implode("\n", $code); return $exported; } /** * Set the number of spaces the exported class should have * * @param integer $numSpaces * @return void */ public function setNumSpaces($numSpaces) { $this->_spaces = str_repeat(' ', $numSpaces); $this->_numSpaces = $numSpaces; } /** * Set the name of the class the generated classes should extend from * * @return void */ public function setClassToExtend($classToExtend) { $this->_classToExtend = $classToExtend; } /** * This method is overriden so that each class is outputted * to the appropriate path where namespaces become directories. * * Export each ClassMetadata instance to a single Doctrine Mapping file * named after the entity * * @return void */ public function export() { if ( ! is_dir($this->_outputDir)) { mkdir($this->_outputDir, 0777); } foreach ($this->_metadatas as $metadata) { $outputPath = $this->_outputDir . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->_extension; $outputDir = dirname($outputPath); if ( ! is_dir($outputDir)) { mkdir($outputDir, 0777, true); } if ( ! file_exists($outputPath)) { $this->_isNew = true; } $this->_outputPath = $outputPath; $output = $this->exportClassMetadata($metadata); file_put_contents($outputPath, $output); } } private function _hasProperty($property, $metadata) { if ($this->_isNew) { return false; } else { return strpos($this->_currentCode, '$' . $property) !== false ? true : false; } } private function _hasMethod($method, $metadata) { if ($this->_isNew) { return false; } else { return strpos($this->_currentCode, 'function ' . $method) !== false ? true : false; } } private function _hasNamespace($metadata) { return strpos($metadata->name, '\\') ? true : false; } private function _extendsClass() { return $this->_classToExtend ? true : false; } private function _getClassToExtend() { return $this->_classToExtend; } private function _getClassToExtendName() { $refl = new \ReflectionClass($this->getClassToExtend()); return $refl->getShortName(); } private function _getClassToExtendNamespace() { $refl = new \ReflectionClass($this->getClassToExtend()); return $refl->getNamespaceName() ? $refl->getNamespaceName():$refl->getShortName(); } private function _getClassName($metadata) { if ($pos = strpos($metadata->name, '\\')) { return substr($metadata->name, $pos + 1, strlen($metadata->name)); } else { return $metadata->name; } } private function _getNamespace($metadata) { return substr($metadata->name, 0, strrpos($metadata->name, '\\')); } private function _addMethod($type, $fieldName, $metadata, array &$methods) { $methodName = $type . ucfirst($fieldName); if ($this->_hasMethod($methodName, $metadata)) { return false; } $method = array(); $method[] = $this->_spaces . '/**'; if ($type == 'get') { $method[] = $this->_spaces . ' * Get ' . $fieldName; } else if ($type == 'set') { $method[] = $this->_spaces . ' * Set ' . $fieldName; } else if ($type == 'add') { $method[] = $this->_spaces . ' * Add ' . $fieldName; } $method[] = $this->_spaces . ' */'; if ($type == 'get') { $method[] = $this->_spaces . 'public function ' . $methodName . '()'; } else if ($type == 'set') { $method[] = $this->_spaces . 'public function ' . $methodName . '($value)'; } else if ($type == 'add') { $method[] = $this->_spaces . 'public function ' . $methodName . '($value)'; } $method[] = $this->_spaces . '{'; if ($type == 'get') { $method[] = $this->_spaces . $this->_spaces . 'return $this->' . $fieldName . ';'; } else if ($type == 'set') { $method[] = $this->_spaces . $this->_spaces . '$this->' . $fieldName . ' = $value;'; } else if ($type == 'add') { $method[] = $this->_spaces . $this->_spaces . '$this->' . $fieldName . '[] = $value;'; } $method[] = $this->_spaces . '}'; $method[] = "\n"; $methods[] = implode("\n", $method); } private function _getMethods($metadata) { $methods = array(); foreach ($metadata->fieldMappings as $fieldMapping) { if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id']) { $this->_addMethod('set', $fieldMapping['fieldName'], $metadata, $methods); } $this->_addMethod('get', $fieldMapping['fieldName'], $metadata, $methods); } foreach ($metadata->associationMappings as $associationMapping) { if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { $this->_addMethod('set', $associationMapping->sourceFieldName, $metadata, $methods); $this->_addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { if ($associationMapping->isOwningSide) { $this->_addMethod('set', $associationMapping->sourceFieldName, $metadata, $methods); $this->_addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); } else { $this->_addMethod('add', $associationMapping->sourceFieldName, $metadata, $methods); $this->_addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); } } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { $this->_addMethod('add', $associationMapping->sourceFieldName, $metadata, $methods); $this->_addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); } } return $methods; } private function _getTableAnnotation($metadata) { $table = array(); $table[] = 'name="' . $metadata->primaryTable['name'] . '"'; if (isset($metadata->primaryTable['schema'])) { $table[] = 'schema="' . $metadata->primaryTable['schema'] . '"'; } return '@Table(' . implode(', ', $table) . ')'; } private function _getJoinColumnAnnotation(array $joinColumn) { $joinColumnAnnot = array(); if (isset($joinColumn['name'])) { $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; } if (isset($joinColumn['referencedColumnName'])) { $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; } if (isset($joinColumn['unique']) && $joinColumn['unique']) { $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false'); } if (isset($joinColumn['nullable'])) { $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); } if (isset($joinColumn['onDelete'])) { $joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false'); } if (isset($joinColumn['onUpdate'])) { $joinColumnAnnot[] = 'onUpdate=' . ($joinColumn['onUpdate'] ? 'true' : 'false'); } return '@JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; } private function _getAssociationMappingAnnotation(AssociationMapping $associationMapping, ClassMetadataInfo $metadata) { $e = explode('\\', get_class($associationMapping)); $type = str_replace('Mapping', '', end($e)); $typeOptions = array(); if (isset($associationMapping->targetEntityName)) { $typeOptions[] = 'targetEntity="' . $associationMapping->targetEntityName . '"'; } if (isset($associationMapping->mappedByFieldName)) { $typeOptions[] = 'mappedBy="' . $associationMapping->mappedByFieldName . '"'; } if (isset($associationMapping->cascades) && $associationMapping->cascades) { $typeOptions[] = 'cascade={"' . implode('"', $associationMapping->cascades) . '"}'; } if (isset($associationMapping->orphanRemoval) && $associationMapping->orphanRemoval) { $typeOptions[] = 'orphanRemoval=' . ($associationMapping->orphanRemoval ? 'true' : 'false'); } $lines = array(); $lines[] = $this->_spaces . '/**'; $lines[] = $this->_spaces . ' * @' . $type . '(' . implode(', ', $typeOptions) . ')'; if (isset($associationMapping->joinColumns) && $associationMapping->joinColumns) { $lines[] = $this->_spaces . ' * @JoinColumns({'; $joinColumnsLines = array(); foreach ($associationMapping->joinColumns as $joinColumn) { if ($joinColumnAnnot = $this->_getJoinColumnAnnotation($joinColumn)) { $joinColumnsLines[] = $this->_spaces . ' * ' . $joinColumnAnnot; } } $lines[] = implode(",\n", $joinColumnsLines); $lines[] = $this->_spaces . ' * })'; } if (isset($associationMapping->joinTable) && $associationMapping->joinTable) { $joinTable = array(); $joinTable[] = 'name="' . $associationMapping->joinTable['name'] . '"'; if (isset($associationMapping->joinTable['schema'])) { $joinTable[] = 'schema="' . $associationMapping->joinTable['schema'] . '"'; } $lines[] = $this->_spaces . ' * @JoinTable(' . implode(', ', $joinTable) . ','; $lines[] = $this->_spaces . ' * joinColumns={'; foreach ($associationMapping->joinTable['joinColumns'] as $joinColumn) { $lines[] = $this->_spaces . ' * ' . $this->_getJoinColumnAnnotation($joinColumn); } $lines[] = $this->_spaces . ' * },'; $lines[] = $this->_spaces . ' * inverseJoinColumns={'; foreach ($associationMapping->joinTable['inverseJoinColumns'] as $joinColumn) { $lines[] = $this->_spaces . ' * ' . $this->_getJoinColumnAnnotation($joinColumn); } $lines[] = $this->_spaces . ' * }'; $lines[] = $this->_spaces . ' * )'; } $lines[] = $this->_spaces . ' */'; return implode("\n", $lines); } private function _getFieldMappingAnnotation(array $fieldMapping, ClassMetadataInfo $metadata) { $lines = array(); $lines[] = $this->_spaces . '/**'; $column = array(); if (isset($fieldMapping['columnName'])) { $column[] = 'name="' . $fieldMapping['columnName'] . '"'; } if (isset($fieldMapping['type'])) { $column[] = 'type="' . $fieldMapping['type'] . '"'; } if (isset($fieldMapping['length'])) { $column[] = 'length=' . $fieldMapping['length']; } if (isset($fieldMapping['precision'])) { $column[] = 'precision=' . $fieldMapping['precision']; } if (isset($fieldMapping['scale'])) { $column[] = 'scale=' . $fieldMapping['scale']; } if (isset($fieldMapping['nullable'])) { $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); } if (isset($fieldMapping['options'])) { $options = array(); foreach ($fieldMapping['options'] as $key => $value) { $value = var_export($value, true); $value = str_replace("'", '"', $value); $options[] = ! is_numeric($key) ? $key . '=' . $value:$value; } if ($options) { $column[] = 'options={' . implode(', ', $options) . '}'; } } if (isset($fieldMapping['unique'])) { $column[] = 'unique=' . var_export($fieldMapping['unique'], true); } $lines[] = $this->_spaces . ' * @Column(' . implode(', ', $column) . ')'; if (isset($fieldMapping['id']) && $fieldMapping['id']) { $lines[] = $this->_spaces . ' * @Id'; if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { $lines[] = $this->_spaces.' * @GeneratedValue(strategy="' . $generatorType . '")'; } if ($metadata->sequenceGeneratorDefinition) { $sequenceGenerator = array(); if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; } if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { $sequenceGenerator[] = 'allocationSize="' . $metadata->sequenceGeneratorDefinition['allocationSize'] . '"'; } if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { $sequenceGenerator[] = 'initialValue="' . $metadata->sequenceGeneratorDefinition['initialValue'] . '"'; } $lines[] = $this->_spaces . ' * @SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; } } if (isset($fieldMapping['version']) && $fieldMapping['version']) { $lines[] = $this->_spaces . ' * @Version'; } $lines[] = $this->_spaces . ' */'; return implode("\n", $lines); } }