/** * @param \PhpParser\Node $node */ public function leaveNode(\PhpParser\Node $node) { array_pop($this->contextStack); if ($this->isContainerNode(end($this->contextStack)) || count($this->contextStack) === 0) { // we are on the first level if ($node instanceof Node\Stmt\Class_) { if (count($this->contextStack) > 0) { if (end($this->contextStack)->getType() == 'Stmt_Namespace') { $currentNamespaceName = NodeConverter::getValueFromNode(end($this->contextStack)); $this->currentClassObject->setNamespaceName($currentNamespaceName); $this->currentNamespace->addClass($this->currentClassObject); } } else { $this->fileObject->addClass($this->currentClassObject); $this->currentClassObject = null; $this->currentContainer = $this->fileObject; } } elseif ($node instanceof Node\Stmt\Namespace_) { if (null !== $this->currentNamespace) { $this->fileObject->addNamespace($this->currentNamespace); $this->currentNamespace = null; $this->currentContainer = $this->fileObject; } else { //TODO: find how this could happen } } elseif ($node instanceof Node\Stmt\TraitUse) { if ($this->currentClassObject) { $this->currentClassObject->addUseTraitStatement($node); } } elseif ($node instanceof Node\Stmt\Use_) { $this->currentContainer->addAliasDeclaration(NodeConverter::convertUseAliasStatementNodeToArray($node)); } elseif ($node instanceof Node\Stmt\ClassConst) { $constants = NodeConverter::convertClassConstantNodeToArray($node); foreach ($constants as $constant) { $this->currentContainer->setConstant($constant['name'], $constant['value']); } } elseif ($node instanceof Node\Stmt\ClassMethod) { $this->onFirstLevel = true; $method = $this->classFactory->buildClassMethodObject($node); $this->currentClassObject->addMethod($method); } elseif ($node instanceof Node\Stmt\Property) { $property = $this->classFactory->buildPropertyObject($node); $this->currentClassObject->addProperty($property); } elseif ($node instanceof Node\Stmt\Function_) { $this->onFirstLevel = true; $function = $this->classFactory->buildFunctionObject($node); $this->currentContainer->addFunction($function); } elseif (!$node instanceof Node\Name) { // any other nodes (except the name node of the current container node) // go into statements container if ($this->currentContainer->getFirstClass() === false) { $this->currentContainer->addPreClassStatements($node); } else { $this->currentContainer->addPostClassStatements($node); } } } }
/** * This method generates the class object, which is passed to the template * it keeps all methods and properties including user modified method bodies and * comments that are required to create a controller class file * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @param boolean $mergeWithExistingClass * * @return \EBT\ExtensionBuilder\Domain\Model\File */ public function generateControllerClassFileObject($domainObject, $controllerClassTemplatePath, $mergeWithExistingClass) { $this->classObject = NULL; $className = $domainObject->getName() . 'Controller'; $this->templateFileObject = $this->parserService->parseFile($controllerClassTemplatePath); $this->templateClassObject = $this->templateFileObject->getFirstClass(); if ($mergeWithExistingClass) { try { $this->classFileObject = $this->roundTripService->getControllerClassFile($domainObject); if (!is_null($this->classFileObject)) { $this->classObject = $this->classFileObject->getFirstClass(); } } catch (\Exception $e) { \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Class ' . $className . ' could not be imported: ' . $e->getMessage(), 'extension_builder'); } } if ($this->classObject == NULL) { $this->classFileObject = clone $this->templateFileObject; $this->classObject = clone $this->templateFileObject->getFirstClass(); $this->classObject->resetAll(); $this->classObject->setName($className); $this->classObject->setDescription($className); if (isset($this->settings['Controller']['parentClass'])) { $parentClass = $this->settings['Controller']['parentClass']; } else { $parentClass = '\\TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController'; } $this->classObject->setParentClassName($parentClass); } if ($domainObject->isAggregateRoot()) { $repositoryName = \TYPO3\CMS\Core\Utility\GeneralUtility::lcfirst($domainObject->getName() . 'Repository'); // now add the property to class Object (or update an existing class Object property) if (!$this->classObject->propertyExists($repositoryName)) { $classProperty = $this->templateClassObject->getProperty('domainObjectRepository'); $classProperty->setName($repositoryName); $classProperty->setDescription($repositoryName); $classProperty->setTag('var', $domainObject->getFullyQualifiedDomainRepositoryClassName(), TRUE); $this->classObject->setProperty($classProperty); } if (!$this->classObject->getProperty($repositoryName)->isTaggedWith('inject') && !$this->classObject->methodExists('inject' . ucfirst($repositoryName))) { $this->classObject->getProperty($repositoryName)->setTag('inject'); } } foreach ($domainObject->getActions() as $action) { $actionMethodName = $action->getName() . 'Action'; if (!$this->classObject->methodExists($actionMethodName)) { $actionMethod = $this->buildActionMethod($action, $domainObject); $this->classObject->addMethod($actionMethod); } } $this->classFileObject->getNamespace()->setName($this->extension->getNamespaceName() . '\\Controller')->setClasses(array($this->classObject)); return $this->classFileObject; }
/** * This method generates the class object, which is passed to the template * it keeps all methods and properties including user modified method bodies and * comments that are required to create a controller class file * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @param \EBT\ExtensionBuilder\Domain\Model\File $existingClassFileObject * * @return \EBT\ExtensionBuilder\Domain\Model\File */ public function generateControllerClassFileObject($domainObject, $controllerClassTemplatePath, $existingClassFileObject = null) { $this->classObject = null; $className = $domainObject->getName() . 'Controller'; $this->templateFileObject = $this->parserService->parseFile($controllerClassTemplatePath); $this->templateClassObject = $this->templateFileObject->getFirstClass(); if ($existingClassFileObject) { $this->classFileObject = $existingClassFileObject; $this->classObject = $existingClassFileObject->getFirstClass(); if ($this->classFileObject->getNamespace() === false) { $nameSpace = new \EBT\ExtensionBuilder\Domain\Model\NamespaceObject('dummy'); $this->classFileObject->addNamespace($nameSpace); } } if ($this->classObject == null) { $this->classFileObject = clone $this->templateFileObject; $this->classObject = clone $this->templateFileObject->getFirstClass(); $this->classObject->resetAll(); $this->classObject->setName($className); $this->classObject->setDescription($className); if (isset($this->settings['Controller']['parentClass'])) { $parentClass = $this->settings['Controller']['parentClass']; } else { $parentClass = '\\TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController'; } $this->classObject->setParentClassName($parentClass); } if ($domainObject->isAggregateRoot()) { $repositoryName = \TYPO3\CMS\Core\Utility\GeneralUtility::lcfirst($domainObject->getName() . 'Repository'); // now add the property to class Object (or update an existing class Object property) if (!$this->classObject->propertyExists($repositoryName)) { $classProperty = $this->templateClassObject->getProperty('domainObjectRepository'); $classProperty->setName($repositoryName); $classProperty->setDescription($repositoryName); $classProperty->setTag('var', $domainObject->getFullyQualifiedDomainRepositoryClassName(), true); $this->classObject->setProperty($classProperty); } if (!$this->classObject->getProperty($repositoryName)->isTaggedWith('inject') && !$this->classObject->methodExists('inject' . ucfirst($repositoryName))) { $this->classObject->getProperty($repositoryName)->setTag('inject'); } } foreach ($domainObject->getActions() as $action) { $actionMethodName = $action->getName() . 'Action'; if (!$this->classObject->methodExists($actionMethodName)) { $actionMethod = $this->buildActionMethod($action, $domainObject); $this->classObject->addMethod($actionMethod); } } $this->classFileObject->getNamespace()->setName($this->extension->getNamespaceName() . '\\Controller')->setClasses(array($this->classObject)); return $this->classFileObject; }
/** * update means renaming of method name, parameter and replacing * parameter names in method body * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject\AbstractProperty $oldProperty * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject\AbstractProperty $newProperty * @param string $methodType get,set,add,remove,is * * @return void */ protected function updateMethod($oldProperty, $newProperty, $methodType) { $oldMethodName = ClassBuilder::getMethodName($oldProperty, $methodType); // the method to be merged $mergedMethod = $this->classObject->getMethod($oldMethodName); if (!$mergedMethod) { // no previous version of the method exists return; } $newMethodName = ClassBuilder::getMethodName($newProperty, $methodType); $this->log('updateMethod:' . $oldMethodName . '=>' . $newMethodName, 'extension_builder'); if ($oldProperty->getName() != $newProperty->getName()) { // rename the method $mergedMethod->setName($newMethodName); $oldMethodBody = $mergedMethod->getBodyStmts(); $oldComment = $mergedMethod->getDocComment(); $newMethodBody = $this->replacePropertyNameInMethodBody($oldProperty->getName(), $newProperty->getName(), $oldMethodBody); $mergedMethod->setBodyStmts($newMethodBody); } // update the method parameters $methodParameters = $mergedMethod->getParameters(); if (!empty($methodParameters)) { $parameterTags = $mergedMethod->getTagValues('param'); foreach ($methodParameters as $methodParameter) { $oldParameterName = $methodParameter->getName(); if ($oldParameterName == ClassBuilder::getParameterName($oldProperty, $methodType)) { $newParameterName = ClassBuilder::getParameterName($newProperty, $methodType); $methodParameter->setName($newParameterName); $newMethodBody = $this->replacePropertyNameInMethodBody($oldParameterName, $newParameterName, $mergedMethod->getBodyStmts()); $mergedMethod->setBodyStmts($newMethodBody); } $typeHint = $methodParameter->getTypeHint(); if ($typeHint) { if ($oldProperty->isRelation()) { /** @var $oldProperty \EBT\ExtensionBuilder\Domain\Model\DomainObject\Relation\AbstractRelation */ if ($typeHint == $oldProperty->getForeignClassName()) { $methodParameter->setTypeHint($this->updateExtensionKey($this->getForeignClassName($newProperty))); } } } $parameterTags[$methodParameter->getPosition()] = ClassBuilder::getParamTag($newProperty, $methodType); $mergedMethod->replaceParameter($methodParameter); } $mergedMethod->setTag('param', $parameterTags); } $returnTagValue = $mergedMethod->getTagValue('return'); if ($returnTagValue != 'void') { $mergedMethod->setTag('return', $newProperty->getTypeForComment() . ' ' . $newProperty->getName()); } // replace property names in description $mergedMethod->setDescription(str_replace($oldProperty->getName(), $newProperty->getName(), $mergedMethod->getDescription())); if ($oldProperty instanceof AbstractRelation && $newProperty instanceof AbstractRelation) { $mergedMethod->setDescription(str_replace($oldProperty->getForeignClassName(), $newProperty->getForeignClassName(), $mergedMethod->getDescription())); } $this->classObject->removeMethod($oldMethodName); $this->classObject->addMethod($mergedMethod); }