Skip to content
This repository was archived by the owner on Jan 31, 2020. It is now read-only.

Add ability to auto-document complex types #53

Merged
merged 8 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/book/wsdl.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,20 @@ binding is bound to the SOAP protocol format.
See the [W3C WSDL documentation section](http://www.w3.org/TR/wsdl#_documentation)
for more details.

## Documenting complex types

To automatically generate documentation for complex types add a class implementing
`Zend\Soap\Wsdl\DocumentationStrategy\DocumentationStrategyInterface` to your
complex type strategy. A `ReflectionDocumentation` strategy is included, which
will parse class and property docblocks and generate documentation based on the
comments found:

```php
$strategy = new ArrayOfTypeSequence();
$strategy->setDocumentationStrategy(new ReflectionDocumentation());
$wsdl = new Zend\Soap\Wsdl('MyService', $myWebServiceUri, $strategy);
```

## Retrieve the final WSDL document

Several methods exist for retrieving the full WSDL definition document:
Expand Down
13 changes: 10 additions & 3 deletions src/Wsdl.php
Original file line number Diff line number Diff line change
Expand Up @@ -548,11 +548,18 @@ public function addDocumentation($inputNode, $documentation)
$node = $inputNode;
}

$doc = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'documentation');
if ($node->namespaceURI == Wsdl::XSD_NS_URI) {
// complex types require annotation element for documentation
$doc = $this->dom->createElementNS(Wsdl::XSD_NS_URI, 'documentation');
$child = $this->dom->createElementNS(Wsdl::XSD_NS_URI, 'annotation');
$child->appendChild($doc);
} else {
$doc = $child = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'documentation');
}
if ($node->hasChildNodes()) {
$node->insertBefore($doc, $node->firstChild);
$node->insertBefore($child, $node->firstChild);
} else {
$node->appendChild($doc);
$node->appendChild($child);
}

$docCData = $this->dom->createTextNode(str_replace(["\r\n", "\r"], "\n", $documentation));
Expand Down
16 changes: 16 additions & 0 deletions src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;

use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\DocumentationStrategy\DocumentationStrategyInterface;

/**
* Abstract class for Zend\Soap\Wsdl\Strategy.
Expand All @@ -22,6 +23,11 @@ abstract class AbstractComplexTypeStrategy implements ComplexTypeStrategyInterfa
*/
protected $context;

/**
* @var DocumentationStrategyInterface
*/
protected $documentationStrategy;

/**
* Set the WSDL Context object this strategy resides in.
*
Expand Down Expand Up @@ -56,4 +62,14 @@ public function scanRegisteredTypes($phpType)
}
return;
}

/**
* Sets the strategy for generating complex type documentation
*
* @param DocumentationStrategyInterface $documentationStrategy
*/
public function setDocumentationStrategy(DocumentationStrategyInterface $documentationStrategy)
{
$this->documentationStrategy = $documentationStrategy;
}
}
27 changes: 26 additions & 1 deletion src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Soap\Wsdl\ComplexTypeStrategy;

use DOMElement;
use ReflectionClass;
use ReflectionProperty;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\DocumentationStrategy\DocumentationStrategyInterface;

class DefaultComplexType extends AbstractComplexTypeStrategy
{
Expand Down Expand Up @@ -69,13 +72,35 @@ public function addComplexType($type)
$element->setAttribute('nillable', 'true');
}

$this->addPropertyDocumentation($property, $element);
$all->appendChild($element);
}
}

$complexType->appendChild($all);
$this->addComplexTypeDocumentation($class, $complexType);
$this->getContext()->getSchema()->appendChild($complexType);

return $soapType;
}

private function addPropertyDocumentation(ReflectionProperty $property, DOMElement $element)
{
if ($this->documentationStrategy instanceof DocumentationStrategyInterface) {
$documentation = $this->documentationStrategy->getPropertyDocumentation($property);
if ($documentation) {
$this->getContext()->addDocumentation($element, $documentation);
}
}
}

private function addComplexTypeDocumentation(ReflectionClass $class, DOMElement $element)
{
if ($this->documentationStrategy instanceof DocumentationStrategyInterface) {
$documentation = $this->documentationStrategy->getComplexTypeDocumentation($class);
if ($documentation) {
$this->getContext()->addDocumentation($element, $documentation);
}
}
}
}
35 changes: 35 additions & 0 deletions src/Wsdl/DocumentationStrategy/DocumentationStrategyInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Soap\Wsdl\DocumentationStrategy;

use ReflectionClass;
use ReflectionProperty;

/**
* Implement this interface to provide contents for <wsdl:documentation> elements on complex types
*/
interface DocumentationStrategyInterface
{
/**
* Returns documentation for complex type property
*
* @param ReflectionProperty $property
* @return string
*/
public function getPropertyDocumentation(ReflectionProperty $property) : string;

/**
* Returns documentation for complex type
*
* @param ReflectionClass $class
* @return string
*/
public function getComplexTypeDocumentation(ReflectionClass $class) : string;
}
48 changes: 48 additions & 0 deletions src/Wsdl/DocumentationStrategy/ReflectionDocumentation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Soap\Wsdl\DocumentationStrategy;

use ReflectionClass;
use ReflectionProperty;

final class ReflectionDocumentation implements DocumentationStrategyInterface
{
public function getPropertyDocumentation(ReflectionProperty $property) : string
{
return $this->parseDocComment($property->getDocComment());
}

public function getComplexTypeDocumentation(ReflectionClass $class) : string
{
return $this->parseDocComment($class->getDocComment());
}

private function parseDocComment(string $docComment) : string
{
$documentation = [];
foreach (explode("\n", $docComment) as $i => $line) {
if ($i == 0) {
continue;
}

$line = trim(preg_replace('/\s*\*+/', '', $line));
if (preg_match('/^(@[a-z]|\/)/i', $line)) {
break;
}

// only include newlines if we've already got documentation
if (! empty($documentation) || $line != '') {
$documentation[] = $line;
}
}

return join("\n", $documentation);
}
}
18 changes: 18 additions & 0 deletions test/Wsdl/DefaultComplexTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@

namespace ZendTest\Soap\Wsdl;

use Prophecy\Argument;
use ReflectionClass;
use ReflectionProperty;
use Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType;
use Zend\Soap\Wsdl\DocumentationStrategy\DocumentationStrategyInterface;
use ZendTest\Soap\TestAsset\PublicPrivateProtected;
use ZendTest\Soap\TestAsset\WsdlTestClass;
use ZendTest\Soap\WsdlTestHelper;

/**
Expand Down Expand Up @@ -56,4 +61,17 @@ public function testDoubleClassesAreDiscoveredByStrategy()

$this->documentNodesTest();
}

public function testDocumentationStrategyCalled()
{
$documentation = $this->prophesize(DocumentationStrategyInterface::class);
$documentation->getPropertyDocumentation(Argument::type(ReflectionProperty::class))
->shouldBeCalledTimes(2)
->willReturn('Property');
$documentation->getComplexTypeDocumentation(Argument::type(ReflectionClass::class))
->shouldBeCalledTimes(1)
->willReturn('Complex type');
$this->strategy->setDocumentationStrategy($documentation->reveal());
$this->strategy->addComplexType(WsdlTestClass::class);
}
}
77 changes: 77 additions & 0 deletions test/Wsdl/DocumentationStrategy/ReflectionDocumentationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Soap\Wsdl\DocumentationStrategy;

use PHPUnit\Framework\TestCase;
use ReflectionClass;
use Zend\Soap\Wsdl\DocumentationStrategy\ReflectionDocumentation;
use ZendTest\Soap\TestAsset\WsdlTestClass;

class ReflectionDocumentationTest extends TestCase
{
/**
* @var ReflectionDocumentation
*/
private $documentation;

protected function setUp()
{
$this->documentation = new ReflectionDocumentation();
}

public function testGetPropertyDocumentationParsesDocComment()
{
$class = new class {
/**
* Property documentation
*/
public $foo;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you remove the scalar type hints and return type hints, you'll also need to update this test to remove anonymous class usage. Create classes under a TestAsset directory, and instantiate them here.


$reflection = new ReflectionClass($class);
$actual = $this->documentation->getPropertyDocumentation($reflection->getProperty('foo'));
$this->assertEquals('Property documentation', $actual);
}

public function testGetPropertyDocumentationSkipsAnnotations()
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to run via phpcs when done; extra lines like this one should be picked up and flagged.

$class = new class {
/**
* Property documentation
* @type int
*/
public $foo;
};

$reflection = new ReflectionClass($class);
$actual = $this->documentation->getPropertyDocumentation($reflection->getProperty('foo'));
$this->assertEquals('Property documentation', $actual);
}

public function testGetPropertyDocumentationReturnsEmptyString()
{

$class = new class {
public $foo;
};

$reflection = new ReflectionClass($class);
$actual = $this->documentation->getPropertyDocumentation($reflection->getProperty('foo'));
$this->assertEquals('', $actual);
}

public function getGetComplexTypeDocumentationParsesDocComment()
{
$reflection = new ReflectionClass(new WsdlTestClass());
$actual = $this->documentation->getComplexTypeDocumentation($reflection);
$this->assertEquals('Test class', $actual);
}
}
14 changes: 14 additions & 0 deletions test/WsdlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,20 @@ public function testAddDocumentationToSetInsertsBefore()
$this->assertEquals('documentation', $nodes->item(0)->nodeName);
}

public function testComplexTypeDocumentationAddedAsAnnotation()
{
$this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import the class and use ::class notation instead of a string.

$nodes = $this->xpath->query('//xsd:complexType[@name="WsdlTestClass"]');

$this->wsdl->addDocumentation($nodes->item(0), 'documentation');

$nodes = $this->xpath->query('//xsd:complexType[@name="WsdlTestClass"]/*[1]');
$this->assertEquals('xsd:annotation', $nodes->item(0)->nodeName);

$nodes = $this->xpath->query('//xsd:complexType[@name="WsdlTestClass"]/xsd:annotation/*[1]');
$this->assertEquals('xsd:documentation', $nodes->item(0)->nodeName);
}

public function testDumpToFile()
{
$file = tempnam(sys_get_temp_dir(), 'zfunittest');
Expand Down