/** * Implements the loading of the class object * * @throws Exception if the class is already generated(not null) */ protected function generateClass() { parent::generateClass(); // If it is child type, fallback to ComplexType. Can check this only when all // types are loaded. See Generator->loadTypes(); if ($this->getBaseTypeClass() === null) { $this->implementArrayInterfaces(); } }
/** * Determine parent class * * @return string|null * Returns a string containing the PHP identifier for the parent class * or null if there is no applicable parent class. */ public function getBaseTypeClass() { // If we have a base type which is different than the current class then extend that. // It is actually possible to have different classes with the same name as PHP SoapClient has a poor // understanding of namespaces. Two types with the same name but in different namespaces will have the same // identifier. if ($this->baseType !== null && $this->baseType !== $this) { return $this->baseType->getPhpIdentifier(); } return null; }
/** * Setup and return a service with operations and types. * * @return Service */ private function givenServiceWithOperations() { // Response GetBook types $responseBookName = new ComplexType($this->config, 'Method_Get_Book_Response_BOOK_BOOK_NAME'); $responseBookName->addMember('string', 'bookName', false); $responseBook = new ComplexType($this->config, 'Method_Get_Book_Response_BOOK'); $responseBook->addMember('int', 'bookId', false); // Base type example $responseBook->setBaseType($responseBookName); $returnGetBookType = new ComplexType($this->config, 'Get_Book_Type_Response'); $returnGetBookType->addMember('Method_Get_Book_Response_BOOK', 'book_response', false); // Request GetBook types $bookType = new Enum($this->config, 'Book_Type_Enumeration', 'string'); $bookType->addValue('fiction'); $bookType->addValue('comedy'); $requestBook = new ComplexType($this->config, 'Method_Get_Book_Request_BOOK'); $requestBook->addMember('int', 'bookId', false); $requestBook->addMember('Book_Type_Enumeration', 'genre', false); $requestGetBook = new ComplexType($this->config, 'Get_Book_Type_Request'); $requestGetBook->addMember('Method_Get_Book_Request_BOOK', 'book_request', false); // Operation GetBook $getBookOperation = new Operation('GetBook', 'Get_Book_Type_Request $request', 'Get Book', 'Get_Book_Type_Response'); // Response GetAuthors type $responseAuthor = new ComplexType($this->config, 'Get_Authors_Response_Author'); $responseAuthor->addMember('int', 'authorId', false); $responseAuthor->addMember('string', 'authorName', false); $returnGetAuthors = new ComplexType($this->config, 'Method_Get_Authors_Response'); $returnGetAuthors->addMember('Get_Authors_Response_Author[]', 'Get_Authors_Response_Author', false); // Request GetAuthors type $requestGetAuthor = new ComplexType($this->config, 'Method_Get_Authors_Request'); $requestGetAuthor->addMember('Method_Get_Book_Request_BOOK', 'book_request', false); // Operation GetAuthors $getAuthorsOperator = new Operation('GetAuthor', 'Method_Get_Authors_Request $request', 'Get Authors', 'Method_Get_Authors_Response'); // Service creation $types = array($responseBookName, $responseBook, $returnGetBookType, $requestBook, $requestGetBook, $responseAuthor, $returnGetAuthors, $requestGetAuthor, $bookType); $service = new Service($this->config, 'Book_Shell', $types, 'Book shells'); $service->addOperation($getBookOperation); $service->addOperation($getAuthorsOperator); return $service; }
/** * Implements the loading of the class object * * @throws Exception if the class is already generated(not null) */ protected function generateClass() { if ($this->class != null) { throw new Exception("The class has already been generated"); } // Determine parent class $classBaseType = null; // If we have a base type which is different than the current class then extend that. // It is actually possible to have different classes with the same name as PHP SoapClient has a poor // understanding of namespaces. Two types with the same name but in different namespaces will have the same // identifier. if ($this->baseType !== null && $this->baseType !== $this) { $classBaseType = $this->baseType->getPhpIdentifier(); } $class = new PhpClass($this->phpIdentifier, false, $classBaseType, null, false, $this->abstract); $constructorComment = new PhpDocComment(); $constructorSource = ''; $constructorParameters = array(); $accessors = array(); // Add base type members to constructor parameter list first and call base class constructor $parentMembers = $this->getBaseTypeMembers($this); if (!empty($parentMembers)) { foreach ($parentMembers as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); if (!$member->getNullable()) { $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorParameters[$name] = Validator::validateTypeHint($type); } } $constructorSource .= ' parent::__construct(' . $this->buildParametersString($constructorParameters, false) . ');' . PHP_EOL; } // Add member variables foreach ($this->members as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); $typeHint = Validator::validateTypeHint($type); $comment = new PhpDocComment(); $comment->setVar(PhpDocElementFactory::getVar($type, $name, '')); $var = new PhpVariable('protected', $name, 'null', $comment); $class->addVariable($var); if (!$member->getNullable()) { if ($type == '\\DateTime') { if ($this->config->get('constructorParamsDefaultToNull')) { $constructorSource .= ' $this->' . $name . ' = $' . $name . ' ? $' . $name . '->format(\\DateTime::ATOM) : null;' . PHP_EOL; } else { $constructorSource .= ' $this->' . $name . ' = $' . $name . '->format(\\DateTime::ATOM);' . PHP_EOL; } } else { $constructorSource .= ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL; } $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorParameters[$name] = $typeHint; } $getterComment = new PhpDocComment(); $getterComment->setReturn(PhpDocElementFactory::getReturn($type, '')); $getterCode = ''; if ($type == '\\DateTime') { $getterCode = ' if ($this->' . $name . ' == null) {' . PHP_EOL . ' return null;' . PHP_EOL . ' } else {' . PHP_EOL . ' try {' . PHP_EOL . ' return new \\DateTime($this->' . $name . ');' . PHP_EOL . ' } catch (\\Exception $e) {' . PHP_EOL . ' return false;' . PHP_EOL . ' }' . PHP_EOL . ' }' . PHP_EOL; } else { $getterCode = ' return $this->' . $name . ';' . PHP_EOL; } $getter = new PhpFunction('public', 'get' . ucfirst($name), '', $getterCode, $getterComment); $accessors[] = $getter; $setterComment = new PhpDocComment(); $setterComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $setterComment->setReturn(PhpDocElementFactory::getReturn($this->phpNamespacedIdentifier, '')); $setterCode = ''; if ($type == '\\DateTime') { if ($member->getNullable()) { $setterCode = ' if ($' . $name . ' == null) {' . PHP_EOL . ' $this->' . $name . ' = null;' . PHP_EOL . ' } else {' . PHP_EOL . ' $this->' . $name . ' = $' . $name . '->format(\\DateTime::ATOM);' . PHP_EOL . ' }' . PHP_EOL; } else { $setterCode = ' $this->' . $name . ' = $' . $name . '->format(\\DateTime::ATOM);' . PHP_EOL; } } else { $setterCode = ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL; } $setterCode .= ' return $this;' . PHP_EOL; $setter = new PhpFunction('public', 'set' . ucfirst($name), $this->buildParametersString(array($name => $typeHint), true, $member->getNullable() && !empty($typeHint)), $setterCode, $setterComment); $accessors[] = $setter; } $constructor = new PhpFunction('public', '__construct', $this->buildParametersString($constructorParameters, true, $this->config->get('constructorParamsDefaultToNull')), $constructorSource, $constructorComment); $class->addFunction($constructor); foreach ($accessors as $accessor) { $class->addFunction($accessor); } $this->class = $class; }
/** * Implements the loading of the class object * * @throws Exception if the class is already generated(not null) */ protected function generateClass() { if ($this->class != null) { throw new Exception("The class has already been generated"); } $class = new PhpClass($this->phpIdentifier, false, $this->baseType !== null ? $this->baseType->getPhpIdentifier() : ''); $constructorComment = new PhpDocComment(); $constructorSource = ''; $constructorParameters = array(); $accessors = array(); // Add base type members to constructor parameter list first and call base class constructor $parentMembers = $this->getBaseTypeMembers($this); if (!empty($parentMembers)) { foreach ($parentMembers as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); if (!$member->getNullable()) { $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorParameters[$name] = Validator::validateTypeHint($type); } } $constructorSource .= ' parent::__construct(' . $this->buildParametersString($constructorParameters, false) . ');' . PHP_EOL; } // Add member variables foreach ($this->members as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); $typeHint = Validator::validateTypeHint($type); $comment = new PhpDocComment(); $comment->setVar(PhpDocElementFactory::getVar($type, $name, '')); $var = new PhpVariable('protected', $name, 'null', $comment); $class->addVariable($var); if (!$member->getNullable()) { if ($type == '\\DateTime') { if ($this->config->get('constructorParamsDefaultToNull')) { // GT Mod - removed DateTime::ATOM date (not compatible with apollo) with Y-m-d\TH:i:s . $constructorSource .= ' $this->' . $name . ' = $' . $name . ' ? $' . $name . '->format(\'Y-m-d\\TH:i:s\\Z\') : null;' . PHP_EOL; } else { // GT Mod - removed DateTime::ATOM date (not compatible with apollo) with Y-m-d\TH:i:s . $constructorSource .= ' $this->' . $name . ' = $' . $name . '->format(\'Y-m-d\\TH:i:s\\Z\');' . PHP_EOL; } } else { $constructorSource .= ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL; } $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorParameters[$name] = $typeHint; } $getterComment = new PhpDocComment(); $getterComment->setReturn(PhpDocElementFactory::getReturn($type, '')); $getterCode = ''; if ($type == '\\DateTime') { $getterCode = ' if ($this->' . $name . ' == null) {' . PHP_EOL . ' return null;' . PHP_EOL . ' } else {' . PHP_EOL . ' try {' . PHP_EOL . ' return new \\DateTime($this->' . $name . ');' . PHP_EOL . ' } catch (\\Exception $e) {' . PHP_EOL . ' return false;' . PHP_EOL . ' }' . PHP_EOL . ' }' . PHP_EOL; } else { $getterCode = ' return $this->' . $name . ';' . PHP_EOL; } $getter = new PhpFunction('public', 'get' . ucfirst($name), '', $getterCode, $getterComment); $accessors[] = $getter; $setterComment = new PhpDocComment(); $setterComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $setterComment->setReturn(PhpDocElementFactory::getReturn($this->phpNamespacedIdentifier, '')); $setterCode = ''; if ($type == '\\DateTime') { // GT Mod - removed DateTime::ATOM date (not compatible with apollo) with Y-m-d\TH:i:sZ . $setterCode = ' $this->' . $name . ' = $' . $name . '->format(\'Y-m-d\\TH:i:s\\Z\');' . PHP_EOL; } else { $setterCode = ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL; } $setterCode .= ' return $this;' . PHP_EOL; $setter = new PhpFunction('public', 'set' . ucfirst($name), $this->buildParametersString(array($name => $typeHint)), $setterCode, $setterComment); $accessors[] = $setter; } $constructor = new PhpFunction('public', '__construct', $this->buildParametersString($constructorParameters, true, $this->config->get('constructorParamsDefaultToNull')), $constructorSource, $constructorComment); $class->addFunction($constructor); foreach ($accessors as $accessor) { $class->addFunction($accessor); } $this->class = $class; }
/** * Loads all type classes */ protected function loadTypes() { $this->log('Loading types'); $types = $this->wsdl->getTypes(); foreach ($types as $typeNode) { $type = null; if ($typeNode->isComplex()) { $type = new ComplexType($this->config, $typeNode->getName()); $this->log('Loading type ' . $type->getPhpIdentifier()); $type->setAbstract($typeNode->isAbstract()); foreach ($typeNode->getParts() as $name => $typeName) { // There are 2 ways a wsdl can indicate that a field accepts the null value - // by setting the "nillable" attribute to "true" or by setting the "minOccurs" attribute to "0". // See http://www.ibm.com/developerworks/webservices/library/ws-tip-null/index.html $nullable = $typeNode->isElementNillable($name) || $typeNode->getElementMinOccurs($name) === 0; $type->addMember($typeName, $name, $nullable); } } elseif ($enumValues = $typeNode->getEnumerations()) { $type = new Enum($this->config, $typeNode->getName(), $typeNode->getRestriction()); array_walk($enumValues, function ($value) use($type) { $type->addValue($value); }); } elseif ($pattern = $typeNode->getPattern()) { $type = new Pattern($this->config, $typeNode->getName(), $typeNode->getRestriction()); $type->setValue($pattern); } if ($type != null) { $already_registered = false; if ($this->config->get('sharedTypes')) { foreach ($this->types as $registered_types) { if ($registered_types->getIdentifier() == $type->getIdentifier()) { $already_registered = true; break; } } } if (!$already_registered) { $this->types[$typeNode->getName()] = $type; } } } // Loop through all types again to setup class inheritance. // We can only do this once all types have been loaded. Otherwise we risk referencing types which have not been // loaded yet. foreach ($types as $type) { if (($baseType = $type->getBase()) && isset($this->types[$baseType]) && $this->types[$baseType] instanceof ComplexType) { $this->types[$type->getName()]->setBaseType($this->types[$baseType]); } } $this->log('Done loading types'); }
/** * Test classes that extend themselves. */ public function testExtendingOwnClass() { // It is actually possible to have a type which extends itself. This is caused by the poor understanding of PHP // namespaces. Two types with the same name but in different namespaces will have the same identifier. $config = new Config(array('inputFile' => null, 'outputDir' => null)); $type = new ComplexType($config, 'ExtendOwn'); $type->setBaseType($type); $this->generateClass($type); $object = new \ExtendOwn(); $class = new \ReflectionClass($object); $this->assertEmpty($class->getParentClass()); }
/** * Test setters for nullable typed members. */ public function testNullableTypedMembers() { $config = new Config(array('inputFile' => null, 'outputDir' => null)); $type = new ComplexType($config, 'NullableDateTime'); // Add a member which has a type (datetime) and is nullable. $type->addMember('datetime', 'aDateTime', true); $this->generateClass($type); $object = new \NullableDateTime(); // If the member is nullable then we should also be able to pass null to the setter without causing an error. $object->setADateTime(null); // Obviously the returned member value should be null as well. $this->assertNull($object->getADateTime()); }
/** * Implements the loading of the class object * * @throws Exception if the class is already generated(not null) */ protected function generateClass() { if ($this->class != null) { throw new Exception("The class has already been generated"); } $class = new PhpClass($this->phpIdentifier, $this->config->getClassExists(), $this->baseType !== null ? $this->baseType->getPhpIdentifier() : ''); // Add the base class as a dependency. Otherwise we risk referencing an undefined class. if (!empty($this->baseType) && !$this->config->getOneFile() && !$this->config->getNoIncludes()) { $class->addDependency($this->baseType->getIdentifier() . '.php'); } $constructorComment = new PhpDocComment(); $constructorComment->setAccess(PhpDocElementFactory::getPublicAccess()); $constructorSource = ''; $constructorParameters = ''; $accessors = array(); // Add base type members to constructor parameter list first and call base class constructor if ($this->baseType !== null) { foreach ($this->baseType->getMembers() as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); if (!$member->getNillable()) { $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorComment->setAccess(PhpDocElementFactory::getPublicAccess()); $constructorParameters .= ', $' . $name; } } $constructorSource .= ' parent::__construct(' . substr($constructorParameters, 2) . ');' . PHP_EOL; } // Add member variables foreach ($this->members as $member) { $type = Validator::validateType($member->getType()); $name = Validator::validateAttribute($member->getName()); $comment = new PhpDocComment(); $comment->setVar(PhpDocElementFactory::getVar($type, $name, '')); $comment->setAccess(PhpDocElementFactory::getPublicAccess()); $var = new PhpVariable('public', $name, 'null', $comment); $class->addVariable($var); if (!$member->getNillable()) { $constructorSource .= ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL; $constructorComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $constructorComment->setAccess(PhpDocElementFactory::getPublicAccess()); $constructorParameters .= ', $' . $name; if ($this->config->getConstructorParamsDefaultToNull()) { $constructorParameters .= ' = null'; } if ($this->config->getCreateAccessors()) { $getterComment = new PhpDocComment(); $getterComment->setReturn(PhpDocElementFactory::getReturn($type, '')); $getter = new PhpFunction('public', 'get' . ucfirst($name), '', ' return $this->' . $name . ';' . PHP_EOL, $getterComment); $accessors[] = $getter; $setterComment = new PhpDocComment(); $setterComment->addParam(PhpDocElementFactory::getParam($type, $name, '')); $setter = new PhpFunction('public', 'set' . ucfirst($name), '$' . $name, ' $this->' . $name . ' = $' . $name . ';' . PHP_EOL, $setterComment); $accessors[] = $setter; } } } $constructorParameters = substr($constructorParameters, 2); // Remove first comma $function = new PhpFunction('public', '__construct', $constructorParameters, $constructorSource, $constructorComment); // Only add the constructor if type constructor is selected if ($this->config->getNoTypeConstructor() == false) { $class->addFunction($function); } foreach ($accessors as $accessor) { $class->addFunction($accessor); } $this->class = $class; }
/** * Loads all type classes */ private function loadTypes() { $this->log('Loading types'); $types = $this->wsdl->getTypes(); foreach ($types as $typeNode) { if ($typeNode->isArray()) { // skip arrays continue; } $type = null; if ($typeNode->isComplex()) { $type = new ComplexType($this->config, $typeNode->getName()); $this->log('Loading type ' . $type->getPhpIdentifier()); foreach ($typeNode->getParts() as $name => $typeName) { $type->addMember($typeName, $name, $typeNode->isElementNillable($name)); } } elseif ($enumValues = $typeNode->getEnumerations()) { $type = new Enum($this->config, $typeNode->getName(), $typeNode->getRestriction()); array_walk($enumValues, function ($value) use($type) { $type->addValue($value); }); } elseif ($pattern = $typeNode->getPattern()) { $type = new Pattern($this->config, $typeNode->getName(), $typeNode->getRestriction()); $type->setValue($pattern); } if ($type != null) { $already_registered = false; if ($this->config->getSharedTypes()) { foreach ($this->types as $registered_types) { if ($registered_types->getIdentifier() == $type->getIdentifier()) { $already_registered = true; break; } } } if (!$already_registered) { $this->types[$typeNode->getName()] = $type; } } } // Loop through all types again to setup class inheritance. // We can only do this once all types have been loaded. Otherwise we risk referencing types which have not been // loaded yet. foreach ($types as $type) { if (($baseType = $type->getBase()) && isset($this->types[$baseType])) { $this->types[$type->getName()]->setBaseType($this->types[$baseType]); } } $this->log('Done loading types'); }
/** * Test fluent setters. */ public function testFluentSetters() { $config = new Config(array('inputFile' => null, 'outputDir' => null)); $complexType = new ComplexType($config, 'Fluent'); $complexType->addMember('string', 'attribute', true); $this->generateClass($complexType); // When calling a setter the returned value should be the same as the // object where the setter was called. $object = new \Fluent(); $returnValue = $object->setAttribute('value'); $this->assertEquals($object, $returnValue); // The setter should also have its own class as its return type. $class = new \ReflectionClass($object); $this->assertMethodHasReturnType($class->getMethod('setAttribute'), $class->getName()); }