Skip to content
This repository has been archived by the owner on Jul 18, 2024. It is now read-only.

Commit

Permalink
[TASK] Refactor SchemaGenerator and remove php: prefix in XSD
Browse files Browse the repository at this point in the history
  • Loading branch information
NamelessCoder committed Jan 19, 2016
1 parent 7e1ca30 commit 48c8c84
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 106 deletions.
62 changes: 62 additions & 0 deletions src/ClassFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
namespace TYPO3Fluid\SchemaGenerator;

/**
* Class ClassFinder
*/
class ClassFinder {

/**
* @param $packagePaths
* @param $phpNamespace
*/
public function getClassNamesInPackages(array $packagePaths) {
$classNames = array();
foreach ($packagePaths as $namespace => $classesPath) {
$classNames = array_replace($classNames, $this->getClassNamesInPackage($classesPath, $namespace));
if (count($classNames) === 0) {
throw new \RuntimeException(sprintf('No ViewHelpers found in path "%s"', $classesPath), 1330029328);
}
}
return $classNames;
}

/**
* Get all class names inside this namespace and return them as array.
* To generate a merged namespace simply provide multiple paths (with
* comma as separator) as $packagePaths argument value.
*
* @param string $packagePath
* @param string $phpNamespace
* @return array
*/
public function getClassNamesInPackage($packagePath, $phpNamespace) {
$allViewHelperClassNames = array();
$affectedViewHelperClassNames = array();

$packagePath = rtrim($packagePath, '/') . '/';
$filesInPath = new \RecursiveDirectoryIterator($packagePath, \RecursiveDirectoryIterator::SKIP_DOTS);
$packagePathLength = strlen($packagePath);
foreach ($filesInPath as $filePathAndFilename) {
$relativePath = substr($filePathAndFilename, $packagePathLength, -4);
$classLocation = str_replace('/', '\\', $relativePath);
$className = $phpNamespace . $classLocation;
if (class_exists($className)) {
$parent = $className;
while ($parent = get_parent_class($parent)) {
array_push($allViewHelperClassNames, $className);
}
}
}
foreach ($allViewHelperClassNames as $viewHelperClassName) {
$classReflection = new \ReflectionClass($viewHelperClassName);
if ($classReflection->isAbstract() === FALSE) {
$affectedViewHelperClassNames[] = $viewHelperClassName;
}
}

sort($affectedViewHelperClassNames);
return $affectedViewHelperClassNames;
}

}
48 changes: 6 additions & 42 deletions src/DocCommentParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
*/
class DocCommentParser {

/**
* @var string The description as found in the doc comment
*/
protected $description = '';

/**
* @var array An array of tag names and their values (multiple values are possible)
*/
protected $tags = array();

/**
* Parses the given doc comment and saves the result (description and
* tags) in the parser's object. They can be retrieved by the
Expand All @@ -25,43 +15,17 @@ class DocCommentParser {
* @return void
*/
public function parseDocComment($docComment) {
$this->description = '';
$this->tags = array();
$parsedDocComment = '';
$lines = explode(chr(10), $docComment);
foreach ($lines as $line) {
if (strlen($line) > 0 && strpos($line, '@') !== FALSE) {
$this->parseTag(substr($line, strpos($line, '@')));
} elseif (count($this->tags) === 0) {
$this->description .= preg_replace('/\\s*\\/?[\\\\*]*(.*)$/', '$1', $line) . chr(10);
continue;
} else {
$parsedDocComment .= preg_replace('/\\s*\\/?[\\\\*]*(.*)$/', '$1', $line) . chr(10);
}
}
$this->description = trim($this->description);
}

/**
* Returns the description which has been previously parsed
*
* @return string The description which has been parsed
*/
public function getDescription() {
return $this->description;
}

/**
* Parses a line of a doc comment for a tag and its value.
* The result is stored in the interal tags array.
*
* @param string $line A line of a doc comment which starts with an @-sign
* @return void
*/
protected function parseTag($line) {
$tagAndValue = preg_split('/\\s/', $line, 2);
$tag = substr($tagAndValue[0], 1);
if (count($tagAndValue) > 1) {
$this->tags[$tag][] = trim($tagAndValue[1]);
} else {
$this->tags[$tag] = array();
}
$parsedDocComment = trim($parsedDocComment);
return $parsedDocComment;
}

}
74 changes: 12 additions & 62 deletions src/SchemaGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,61 +43,17 @@ public function generateXsd(array $namespaceClassPathMap) {
$xmlRootNode = new \SimpleXMLElement(
'<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:php="http://www.php.net/"
targetNamespace="' . $xsdNamespace . '">
</xsd:schema>'
);
$classNames = array();
foreach ($namespaceClassPathMap as $namespace => $classesPath) {
$classNames = array_replace($classNames, $this->getClassNamesInPackage($classesPath, $namespace));
if (count($classNames) === 0) {
throw new \RuntimeException(sprintf('No ViewHelpers found in path "%s"', $classesPath), 1330029328);
}
}
$classFinder = new ClassFinder();
$classNames = $classFinder->getClassNamesInPackages($namespaceClassPathMap);
foreach ($classNames as $className) {
$this->generateXmlForClassName($className, $xmlRootNode);
$this->generateXmlForClassName(new ViewHelperDocumentation($className), $xmlRootNode);
}
return $xmlRootNode->asXML();
}

/**
* Get all class names inside this namespace and return them as array.
* To generate a merged namespace simply provide multiple paths (with
* comma as separator) as $packagePaths argument value.
*
* @param string $packagePath
* @param string $phpNamespace
* @return array
*/
protected function getClassNamesInPackage($packagePath, $phpNamespace) {
$allViewHelperClassNames = array();
$affectedViewHelperClassNames = array();

$packagePath = rtrim($packagePath, '/') . '/';
$filesInPath = new \RecursiveDirectoryIterator($packagePath, \RecursiveDirectoryIterator::SKIP_DOTS);
$packagePathLength = strlen($packagePath);
foreach ($filesInPath as $filePathAndFilename) {
$relativePath = substr($filePathAndFilename, $packagePathLength, -4);
$classLocation = str_replace('/', '\\', $relativePath);
$className = $phpNamespace . $classLocation;
if (class_exists($className)) {
$parent = $className;
while ($parent = get_parent_class($parent)) {
array_push($allViewHelperClassNames, $className);
}
}
}
foreach ($allViewHelperClassNames as $viewHelperClassName) {
$classReflection = new \ReflectionClass($viewHelperClassName);
if ($classReflection->isAbstract() === FALSE) {
$affectedViewHelperClassNames[] = $viewHelperClassName;
}
}

sort($affectedViewHelperClassNames);
return $affectedViewHelperClassNames;
}

/**
* Get a tag name for a given ViewHelper class.
* Example: For the View Helper Tx_Fluid_ViewHelpers_Form_SelectViewHelper, and the
Expand Down Expand Up @@ -138,19 +94,18 @@ protected function addChildWithCData(\SimpleXMLElement $parentXmlNode, $childNod
/**
* Generate the XML Schema for a given class name.
*
* @param string $className Class name to generate the schema for.
* @param ViewHelperDocumentation $documentation Class name to generate the schema for.
* @param \SimpleXMLElement $xmlRootNode XML root node where the xsd:element is appended.
* @return void
*/
protected function generateXmlForClassName($className, \SimpleXMLElement $xmlRootNode) {
$reflectionClass = new \ReflectionClass($className);
if ($reflectionClass->implementsInterface(ViewHelperInterface::class)) {
$tagName = $this->getTagNameForClass($className);
protected function generateXmlForClassName(ViewHelperDocumentation $documentation, \SimpleXMLElement $xmlRootNode) {
if ($documentation->isIncluded()) {
$tagName = $this->getTagNameForClass($documentation->getClass());

$xsdElement = $xmlRootNode->addChild('xsd:element');
$xsdElement['name'] = $tagName;
$this->docCommentParser->parseDocComment($reflectionClass->getDocComment());
$this->addDocumentation($this->docCommentParser->getDescription(), $xsdElement);

$this->addDocumentation($documentation->getDescription(), $xsdElement);

$xsdComplexType = $xsdElement->addChild('xsd:complexType');
$xsdComplexType['mixed'] = 'true';
Expand All @@ -159,7 +114,7 @@ protected function generateXmlForClassName($className, \SimpleXMLElement $xmlRoo
$xsdAny['minOccurs'] = '0';
$xsdAny['maxOccurs'] = '1';

$this->addAttributes($className, $xsdComplexType);
$this->addAttributes($documentation, $xsdComplexType);
}

}
Expand All @@ -172,19 +127,14 @@ protected function generateXmlForClassName($className, \SimpleXMLElement $xmlRoo
* @param \SimpleXMLElement $xsdElement XML element to add the attributes to.
* @return void
*/
protected function addAttributes($className, \SimpleXMLElement $xsdElement) {
$viewHelper = new $className();
/** @var ArgumentDefinition[] $argumentDefinitions */
$argumentDefinitions = $viewHelper->prepareArguments();

foreach ($argumentDefinitions as $argumentDefinition) {
protected function addAttributes(ViewHelperDocumentation $documentation, \SimpleXMLElement $xsdElement) {
foreach ($documentation->getArgumentDefinitions() as $argumentDefinition) {
$default = $argumentDefinition->getDefaultValue();
$type = $argumentDefinition->getType();
$xsdAttribute = $xsdElement->addChild('xsd:attribute');
$xsdAttribute['type'] = $this->convertPhpTypeToXsdType($type);
$xsdAttribute['name'] = $argumentDefinition->getName();
$xsdAttribute['default'] = var_export($default, TRUE);
$xsdAttribute['php:type'] = $type;
if ($argumentDefinition->isRequired()) {
$xsdAttribute['use'] = 'required';
}
Expand Down
57 changes: 57 additions & 0 deletions src/ViewHelperDocumentation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
namespace TYPO3Fluid\SchemaGenerator;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface;

/**
* Class ViewHelperDocumentation
*/
class ViewHelperDocumentation {

/**
* @var string
*/
protected $className;

/**
* @param string $className
*/
public function __construct($className) {
$this->className = $className;
}

/**
* @return string
*/
public function getClass() {
return $this->className;
}

/**
* Returns TRUE if the class should be included in the schema, FALSE otherwise.
*
* @return boolean
*/
public function isIncluded() {
$reflectionClass = new \ReflectionClass($this->className);
return $reflectionClass->implementsInterface(ViewHelperInterface::class);
}

/**
* @return string
*/
public function getDescription() {
$reflectionClass = new \ReflectionClass($this->className);
$docCommentParser = new DocCommentParser();
return $docCommentParser->parseDocComment($reflectionClass->getDocComment());
}

/**
* @return ArgumentDefinition[]
*/
public function getArgumentDefinitions() {
$className = $this->className;
$viewHelper = new $className();
return $viewHelper->prepareArguments();
}

}
3 changes: 1 addition & 2 deletions tests/Unit/SchemaGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ public function getTagNameForClassTestValues() {
* @test
*/
public function testGenerateXsdErrorsWhenNoViewHelpersInPackage() {
$service = $this->getMock(SchemaGenerator::class, array('getClassNamesInPackage'));
$service->expects($this->once())->method('getClassNamesInPackage')->willReturn(array());
$service = $this->getMock(SchemaGenerator::class, array('dummy'));
$this->setExpectedException('RuntimeException');
$service->generateXsd(array('TYPO3Fluid\\SchemaGenerator'));
}
Expand Down

0 comments on commit 48c8c84

Please sign in to comment.