public function afterCompile(Nette\PhpGenerator\ClassType $class) { if ($this->config['parentClass']) { $class->setExtends($this->config['parentClass']); } $initialize = $class->getMethod('initialize'); $builder = $this->getContainerBuilder(); if ($this->debugMode && $this->config['debugger']) { Nette\Bridges\DITracy\ContainerPanel::$compilationTime = $this->time; $initialize->addBody($builder->formatPhp('?;', [new Nette\DI\Statement('@Tracy\\Bar::addPanel', [new Nette\DI\Statement(Nette\Bridges\DITracy\ContainerPanel::class)])])); } foreach (array_filter($builder->findByTag('run')) as $name => $on) { $initialize->addBody('$this->getService(?);', [$name]); } }
/** * [generateClassType description]. * * @param string $properties elementi possibili 'fields', 'extend', 'implements' * @param array $typesReference [description] * @param array $typesDescription [description] * @param ClassConfig $config [description] */ public function generateClassType(array $properties, $typesReference, $typesDescription, ClassConfig $config) { $phpNamespace = $this->currentClass->getNamespace(); if ($config->isInterface) { $this->info('Passo a interfaccia', [$this->currentClass->getName()]); $docs = $this->currentClass->getComment(); $this->currentClass = $this->currentFile->addInterface($phpNamespace->getName() . '\\' . ucfirst($this->currentClass->getName())); $this->currentClass->setComment($docs); $this->info('Check haveConstructor, in caso metto a false', [$config->haveConstructor]); $config->haveConstructor = false; } $this->info('Generate', ['class' => $this->currentClass->getName(), 'namespace' => $phpNamespace->getName(), 'comment' => $this->currentClass->getComment(), 'properties' => $properties]); // extend class if (array_key_exists('extend', $properties)) { $extendClassName = $properties['extend']; $this->info('Aggiungo extend', ['class' => $this->currentClass->getName(), 'extend' => $extendClassName]); $this->currentClass->setExtends($extendClassName); $this->currentClass->getNamespace()->addUse($extendClassName); } // implements class if (array_key_exists('implements', $properties)) { $implementsList = []; if (!is_array($properties['implements'])) { $implementsList[] = $properties['implements']; } else { $implementsList = array_merge($implementsList, $properties['implements']); } $this->currentClass->setImplements($implementsList); foreach ($implementsList as $implementUse) { $this->info('Aggiungo implement', ['class' => $this->currentClass->getName(), 'implements' => $implementUse]); $this->currentClass->getNamespace()->addUse($implementUse); } } // traits if (array_key_exists('traits', $properties)) { if (is_array($properties['traits'])) { foreach ($properties['traits'] as $trait) { $this->addTrait($trait, $typesReference); } } else { $traitObject = $properties['traits']; $this->addTrait($traitObject, $typesReference); } } if ($config->isFinalClass) { $this->currentClass->setFinal(true); } $first = true; if (array_key_exists('fields', $properties)) { /** @var $methodConstructor \Nette\PhpGenerator\Method */ $methodConstructor = null; if ($config->haveConstructor) { $methodConstructor = $this->addConstructor(); } $body = ''; foreach ($properties['fields'] as $name => $fieldProperties) { $isStatic = false; $isAutoinizialize = false; $defaultValue = null; if (array_key_exists('static', $fieldProperties)) { $isStatic = $fieldProperties['static']; } if (array_key_exists('autoinizialize', $fieldProperties)) { $isAutoinizialize = boolval($fieldProperties['autoinizialize']); } if (array_key_exists('default', $fieldProperties)) { $defaultValue = $fieldProperties['default']; } if (!$isAutoinizialize) { if (null != $defaultValue) { //TODO: usare "primitive type per determinare il corretto IF" //FARE UN TEST PER I BOOLEAN //@see https://www.virendrachandak.com/techtalk/php-isset-vs-empty-vs-is_null/ $body .= 'if ( empty($' . $name . ') ) { ' . "\n"; if ($isStatic) { $body .= ' self::$'; } else { $body .= ' $this->'; } $body .= $name . ' = ' . $defaultValue . ';' . "\n"; $body .= '} else {'; if ($isStatic) { $body .= ' self::$'; } else { $body .= ' $this->'; } $body .= $name . ' = $' . $name . ';' . "\n"; $body .= '}' . "\n"; } else { if (!$isStatic) { $body .= ' $this->' . $name . ' = $' . $name . ';' . "\n"; } } } else { if (!empty($defaultValue) || is_int($defaultValue)) { if (substr(rtrim($defaultValue), -1) == ';') { $this->error('autoinizialize for ' . $name . ' on class ' . $this->currentClass->getName() . ' have default with ";" please remove!'); $defaultValue = substr($defaultValue, 0, strlen($defaultValue) - 1); } if (!$isStatic) { if ($isAutoinizialize) { $body .= '// autoinizialize' . "\n"; $body .= '$this->' . $name . ' = ' . $defaultValue . ';' . "\n"; } else { if ($defaultValue) { $body .= 'if ( !is_null($' . $name . ') ) {' . "\n"; $body .= ' $this->' . $name . ' = $' . $name . ';' . "\n"; $body .= '} else {' . "\n"; $body .= ' $this->' . $name . ' = ' . $defaultValue . ';' . "\n"; $body .= '}' . "\n"; // $body .= '$this->'.$name.' = '.$defaultValue.';'."\n"; } else { $body .= 'if ( is_null($' . $name . ') ) {' . "\n"; $body .= ' $this->' . $name . ' = ' . $defaultValue . ';' . "\n"; $body .= '}' . "\n"; } } } } else { $this->error('autoinizialize for ' . $name . ' not defined on element ' . $this->currentClass->getName()); $this->errors[] = 'autoinizialize for ' . $name . ' not defined on element ' . $this->currentClass->getName(); } } $fieldClassFull = ''; if (array_key_exists('class', $fieldProperties)) { $fieldClassName = ucfirst($fieldProperties['class']); if (array_key_exists($fieldClassName, $typesReference)) { $fieldNamespace = $typesReference[$fieldClassName]; $fieldClassFull = $fieldNamespace . '\\' . $fieldClassName; $this->info('Trovato field namespace tra le reference', ['class' => $this->currentClass->getName(), 'field' => $fieldClassName, 'className' => $fieldClassFull]); } else { //FIXME: strpos is better if ($fieldClassName[0] == '\\') { //Class: \DateTime $fieldClassFull = $fieldClassName; } else { $fieldClassFull = $phpNamespace->getName() . '\\' . $fieldClassName; $this->info('Uso class for field same namespace', ['class' => $this->currentClass->getName(), 'field' => $fieldClassName, 'className' => $fieldClassFull]); } } if ($config->haveConstructor && !$isStatic) { $parameter = null; if (!$isAutoinizialize) { $this->info('Aggiungo parametro al costruttore', ['class' => $this->currentClass->getName(), 'parameter' => $name, 'className' => $fieldClassFull, 'default' => $defaultValue, 'autoinizialize' => $isAutoinizialize]); if (!$first) { $parameter = $methodConstructor->addParameter($name, null); //solo i primitivi hanno un default, gli altri null come object $parameter->setTypeHint($fieldClassFull); } else { $parameter = $methodConstructor->addParameter($name); $parameter->setTypeHint($fieldClassFull); } } else { $this->info('Skip parametro al costruttore -> autoinizialize true', ['class' => $this->currentClass->getName(), 'parameter' => $name, 'className' => $fieldClassFull, 'default' => $defaultValue, 'autoinizialize' => $isAutoinizialize]); } } if (array_key_exists($fieldClassName, $typesReference)) { $this->info('Add field type class with namespace', ['class' => $this->currentClass->getName(), 'field' => $fieldClassName, 'className' => $fieldClassFull]); $this->currentClass->getNamespace()->addUse($fieldClassFull); } } else { //tipo primitivo $fieldClassName = $fieldProperties['primitive']; $fieldNamespace = null; $fieldClassFull = $fieldProperties['primitive']; if ($config->haveConstructor && !$isStatic) { //FIXME: se sono in php7 ho anche gli altri elementi primitivi //@see: http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration $parameter = null; if (!$isAutoinizialize) { if (is_null($defaultValue)) { $this->info('Aggiungo parametro al costruttore', ['class' => $this->currentClass->getName(), 'parameter' => $name, 'className' => $fieldClassFull, 'default' => $defaultValue, 'autoinizialize' => $isAutoinizialize]); //PHP7 ONLY // if ($fieldClassFull == 'int') { // $parameter->setTypeHint('int'); // } if (!$first) { $parameter = $methodConstructor->addParameter($name, null); } else { $parameter = $methodConstructor->addParameter($name); } if ($fieldClassFull == 'array') { $parameter->setTypeHint('array'); } else { if ($defaultValue != null) { /* @var $parameter \Nette\PhpGenerator\Parameter */ $parameter->setDefaultValue('' . $defaultValue); } } } } } } $this->info('Check autoinizialize field', ['class' => $this->currentClass->getName(), 'field' => $name, 'autoinizialize' => $isAutoinizialize, 'default' => $defaultValue]); $comment = 'no description available'; if (array_key_exists('description', $fieldProperties)) { $comment = $fieldProperties['description']; } else { if (!is_null($typesDescription) && array_key_exists($fieldClassName, $typesDescription)) { $comment = $typesDescription[$fieldClassName]; } } if (!$config->isInterface) { /** $field @var \Nette\PhpGenerator\Property */ $field = $this->currentClass->addProperty($name); $field->setStatic($isStatic); if ($config->isEnum) { $field->setVisibility('protected'); } else { $field->setVisibility('private'); } $field->addComment($comment)->addComment('@var ' . $fieldClassFull); } $createSetter = $config->haveSetter; if (array_key_exists('setter', $fieldProperties)) { $createSetter = $fieldProperties['setter']; } $createGetter = $config->haveGetter; if (array_key_exists('getter', $fieldProperties)) { $createGetter = $fieldProperties['getter']; } if ($config->isInterface) { if ($createGetter) { $this->addGetter($name, $fieldClassFull, $isStatic, false); } if ($createSetter) { $this->addSetter($name, $fieldClassFull, $isStatic, false); } } else { if ($createGetter) { $this->addGetter($name, $fieldClassFull, $isStatic, true); } if ($createSetter) { $this->addSetter($name, $fieldClassFull, $isStatic, true); } } if (!$isAutoinizialize) { $first = false; } } if ($config->haveConstructor) { $methodConstructor->setBody($body, []); } } //end fields if (array_key_exists('methods', $properties)) { $body = ''; foreach ($properties['methods'] as $methodName => $methodsProperties) { $this->info('Aggiungo method', ['class' => $this->currentClass->getName(), 'methodName' => $methodName, 'methodProp' => $methodsProperties]); /** $newMethodCall @var \Nette\PhpGenerator\Method */ $newMethodCall = $this->currentClass->addMethod($methodName); $newMethodCall->setFinal(true); $newMethodCall->setStatic(false); if (array_key_exists('static', $methodsProperties)) { $newMethodCall->setStatic($methodsProperties['static']); } if (array_key_exists('description', $methodsProperties)) { $newMethodCall->setVisibility($methodsProperties['visibility']); } else { $newMethodCall->setVisibility('public'); } if (array_key_exists('description', $methodsProperties)) { $newMethodCall->addComment($methodsProperties['description']); } else { $returnType = 'void'; if (array_key_exists('@return', $methodsProperties)) { $returnType = $methodsProperties['@return']; //TODO: .'|null' va messo in quale condizione? $newMethodCall->addComment('@return ' . $returnType); } else { //NOPE } } if (array_key_exists('params', $methodsProperties)) { foreach ($methodsProperties['params'] as $paramName => $paramProp) { if (array_key_exists('class', $paramProp)) { $newMethodCall->addParameter($paramName)->setTypeHint($paramProp['class']); } if (array_key_exists('primitive', $paramProp)) { $newMethodCall->addParameter($paramName); } } } $body = ' // FIMXE: da implementare '; if (array_key_exists('body', $methodsProperties)) { $body = $methodsProperties['body']; } $newMethodCall->setBody($body); } } if ($config->isEnum) { $this->currentClass->setAbstract(true); $this->addSingleton('Singleton instance for enum', false); $this->addParseString(); } if ($config->isSingleton) { $this->addSingleton('Singleton instance', true); } }
public function generateTests($outputFolder) { $container = \Testbench\ContainerFactory::create(FALSE); $presenterFactory = $container->getByType('Nette\\Application\\IPresenterFactory'); $presenters = $container->findByType('Nette\\Application\\UI\\Presenter'); foreach ($presenters as $presenter) { $this->renderMethods = $this->handleMethods = $this->componentMethods = []; /** @var \Nette\Application\UI\Presenter $service */ $service = $container->getService($presenter); if ($service instanceof \Testbench\Mocks\PresenterMock) { continue; } if ($service instanceof \KdybyModule\CliPresenter) { //Oh, Kdyby! :-( continue; } $rc = new \ReflectionClass($service); $renderPrefix = $service->formatActionMethod('') . '|' . $service->formatRenderMethod(''); $methods = $rc->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $methodName = $method->getName(); if (preg_match("~^({$renderPrefix})[a-z0-9]+~i", $methodName)) { try { $optionalArgs = $this->tryCall($service, $methodName, $service->getParameters(), TRUE); if (preg_match('~.*rss.*~i', $methodName)) { $this->renderMethods[$methodName] = 'rss'; } elseif (preg_match('~.*sitemap.*~i', $methodName)) { $this->renderMethods[$methodName] = 'sitemap'; } else { $requiredArgs = $this->tryCall($service, $methodName, $service->getParameters(), FALSE); $this->renderMethods[$methodName] = ['action', [$optionalArgs, $requiredArgs]]; } } catch (\Nette\Application\AbortException $exc) { $this->renderMethods[$methodName] = ['action', $this->getResponse($service)]; } catch (\Exception $exc) { $this->renderMethods[$methodName] = ['exception', $exc]; } } if (preg_match('~^handle[a-z0-9]+~i', $methodName)) { if ($methodName === 'handleInvalidLink') { //internal method continue; } $this->handleMethods[] = $methodName; } if (preg_match('~^createComponent[a-z0-9]+~i', $methodName)) { $method->setAccessible(TRUE); $form = $method->invoke($service); if ($form instanceof \Nette\Application\UI\Form) { $this->componentMethods[$methodName] = $form; } } } $testClassName = $rc->getShortName() . 'Test'; $testClass = new PhpGenerator\ClassType($testClassName); $testClass->setExtends('\\Tester\\TestCase'); $testClass->addTrait('\\Testbench\\TPresenter'); $testClass->addComment('@testCase'); foreach ($this->renderMethods as $testMethod => $testMethodType) { $generatedMethod = $testClass->addMethod('test' . ucfirst($testMethod)); $destination = $presenterFactory->unformatPresenterClass($rc->getName()) . ':'; $destination .= lcfirst(preg_replace('~^(action|render)([a-z]+)~i', '$2', $testMethod)); $extra = NULL; if (is_array($testMethodType)) { /** @var \Exception|\Nette\Application\IResponse $extra */ $extra = $testMethodType[1]; $testMethodType = $testMethodType[0]; //FIXME: fuj, hnus } switch ($testMethodType) { case 'rss': $generatedMethod->addBody('$this->checkRss(?);', [$destination]); break; case 'sitemap': $generatedMethod->addBody('$this->checkSitemap(?);', [$destination]); break; case 'action': if ($extra instanceof \Nette\Application\Responses\RedirectResponse) { $url = new \Nette\Http\Url($extra->getUrl()); $generatedMethod->addBody('$this->checkRedirect(?, ?);', [$destination, $url->getPath()]); } elseif ($extra instanceof \Nette\Application\Responses\JsonResponse) { $generatedMethod->addBody('$this->checkJson(?);', [$destination]); } else { if ($extra[0]) { $generatedMethod->addBody('//FIXME: parameters may not be correct'); $generatedMethod->addBody("\$this->checkAction(?, ?);\n", [$destination, $extra[0]]); $generatedMethod->addBody('$this->checkAction(?, ?);', [$destination, $extra[1]]); } else { $generatedMethod->addBody('$this->checkAction(?);', [$destination]); } } break; case 'exception': $this->generateExceptionBody($generatedMethod, $destination, $extra); break; } } foreach ($this->handleMethods as $testMethod) { $destination = $presenterFactory->unformatPresenterClass($rc->getName()); $action = lcfirst(preg_replace('~^handle([a-z]+)~i', '$1', $testMethod)); $testClass->addMethod('test' . ucfirst($testMethod))->addBody('$this->checkSignal(?, ?);', [$destination . ':', $action]); } foreach ($this->componentMethods as $testMethod => $form) { $destination = $presenterFactory->unformatPresenterClass($rc->getName()); $action = lcfirst(preg_replace('~^createComponent([a-z]+)~i', '$1', $testMethod)); $controls = ''; /** @var \Nette\Application\UI\Form $form */ foreach ($form->getControls() as $control) { if ($control->getName() === '_token_' || $control instanceof \Nette\Forms\Controls\SubmitButton) { continue; } $value = "'###', //FIXME: replace with value"; if ($control instanceof \Nette\Forms\Controls\Checkbox) { $value = 'FALSE'; } $controls .= "\t'" . $control->getName() . "' => {$value}\n"; } try { $form->onSuccess($form, $form->getValues()); $testClass->addMethod('test' . ucfirst($testMethod))->addBody("\$this->checkForm(?, ?, [\n" . $controls . '], ?);', [$destination . ':', $action, FALSE]); } catch (\Nette\Application\AbortException $exc) { $extra = $this->getResponse($service); $path = $extra ? (new \Nette\Http\Url($extra->getUrl()))->getPath() : '/'; $testClass->addMethod('test' . ucfirst($testMethod))->addBody("\$this->checkForm(?, ?, [\n" . $controls . '], ?);', [$destination . ':', $action, $path]); } catch (\Exception $exc) { //This sucks but we have to move on - failure is not an option } } $namespace = $rc->getNamespaceName(); $namespace = $namespace ? '\\' . $namespace : ''; $generatedTest = "<?php\n\nnamespace Tests{$namespace};\n\nuse Tester\\Assert;\n\n"; $depth = substr_count($namespace, '\\'); $levelsUp = str_repeat('../', $depth); $generatedTest .= "require __DIR__ . '/{$levelsUp}bootstrap.php';\n\n"; $generatedTest .= $testClass; $generatedTest .= "\n(new {$testClassName})->run();\n"; $testFileName = preg_replace('~\\\\~', DIRECTORY_SEPARATOR, get_class($service)); \Nette\Utils\FileSystem::write($outputFolder . '/' . $testFileName . '.phpt', $generatedTest); \Nette\Utils\FileSystem::createDir($outputFolder . '/_temp'); \Nette\Utils\FileSystem::write($outputFolder . '/tests.neon', <<<'NEON' # WARNING: it is CRITICAL that this file & directory are NOT accessible directly via a web browser! # http://nette.org/security-warning # (this is just an example) routing: routes: '/x/y[[[/<presenter>]/<action>][/<id>]]': 'Presenter:default' NEON ); } }
/** * @param $className */ private function createMapper($className) { $postfix = 'Mapper'; $class = new ClassType($className . $postfix, $this->namespace); $class->setExtends('Nextras\\Orm\\Mapper\\Mapper'); $this->createClass($class, $className); }
/** * @param string $tableName * @param array $columns * @return void */ protected function generateTableGeneratedHyperRow($tableName, $columns) { $classFqn = $this->config['classes']['row']['generated']; $classFqn = Helpers::substituteClassWildcard($classFqn, $tableName); $className = Helpers::extractClassName($classFqn); $classNamespace = Helpers::extractNamespace($classFqn); $extendsFqn = $this->config['classes']['row']['base']; $extends = Helpers::formatClassName($extendsFqn, $classNamespace); $class = new ClassType($className); $class->setExtends($extends); // Add property annotations based on columns foreach ($columns as $column => $type) { if (in_array($type, [IStructure::FIELD_DATETIME, IStructure::FIELD_TIME, IStructure::FIELD_DATE, IStructure::FIELD_UNIX_TIMESTAMP])) { $type = '\\Nette\\Utils\\DateTime'; } $class->addComment("@property-read {$type} \${$column}"); } // Generate methods.row.getter foreach ((array) $this->config['methods']['row']['getter'] as $methodTemplate) { // Generate column getters foreach ($columns as $column => $type) { if (in_array($type, [IStructure::FIELD_DATETIME, IStructure::FIELD_TIME, IStructure::FIELD_DATE, IStructure::FIELD_UNIX_TIMESTAMP])) { $type = '\\Nette\\Utils\\DateTime'; } $methodName = Helpers::substituteMethodWildcard($methodTemplate, $column); $returnType = $type; $class->addMethod($methodName)->addBody('return $this->activeRow->?;', [$column])->addComment("@return {$returnType}"); // Add property annotation if (Strings::startsWith($methodName, 'get')) { $property = Strings::firstLower(Strings::substring($methodName, 3)); if ($property != $column) { $class->addComment("@property-read {$type} \${$property}"); } } } } // Generate methods.row.ref foreach ((array) $this->config['methods']['row']['ref'] as $methodTemplate) { // Generate 'ref' methods foreach ($this->structure->getBelongsToReference($tableName) as $referencingColumn => $referencedTable) { if (is_array($this->config['tables']) && !in_array($referencedTable, $this->config['tables'])) { continue; } $result = Helpers::underscoreToCamelWithoutPrefix(Strings::replace($referencingColumn, '~_id$~'), $tableName); $methodName = Helpers::substituteMethodWildcard($methodTemplate, $result); $returnType = $this->getTableClass('row', $referencedTable, $classNamespace); $class->addMethod($methodName)->addBody('return $this->ref(?, ?);', [$referencedTable, $referencingColumn])->addComment("@return {$returnType}"); // Add property annotations if (Strings::startsWith($methodName, 'get')) { $property = Strings::firstLower(Strings::substring($methodName, 3)); $class->addComment("@property-read {$returnType} \${$property}"); } } } // Generate methods.row.related foreach ((array) $this->config['methods']['row']['related'] as $methodTemplate) { // Generate 'related' methods foreach ($this->structure->getHasManyReference($tableName) as $relatedTable => $referencingColumns) { if (is_array($this->config['tables']) && !in_array($relatedTable, $this->config['tables'])) { continue; } foreach ($referencingColumns as $referencingColumn) { // Omit longest common prefix between $relatedTable and (this) $tableName $result = Helpers::underscoreToCamelWithoutPrefix($relatedTable, $tableName); if (count($referencingColumns) > 1) { $suffix = 'As' . Helpers::underscoreToCamel(Strings::replace($referencingColumn, '~_id$~')); } else { $suffix = NULL; } $methodName = Helpers::substituteMethodWildcard($methodTemplate, $result, $suffix); $returnType = $this->getTableClass('selection', $relatedTable, $classNamespace); $class->addMethod($methodName)->addBody('return $this->related(?, ?);', [$relatedTable, $referencingColumn])->addComment("@return {$returnType}"); // Add property annotations if (Strings::startsWith($methodName, 'get')) { $property = Strings::firstLower(Strings::substring($methodName, 3)); $class->addComment("@property-read {$returnType} \${$property}"); } } } } $code = implode("\n\n", ['<?php', "/**\n * This is a generated file. DO NOT EDIT. It will be overwritten.\n */", "namespace {$classNamespace};", $class]); $dir = $this->config['dir'] . '/' . 'tables' . '/' . $tableName; $file = $dir . '/' . $className . '.php'; $this->writeIfChanged($file, $code); }