Skip to content

Commit

Permalink
extractor system
Browse files Browse the repository at this point in the history
  • Loading branch information
mpoiriert committed Mar 29, 2015
1 parent 07b722d commit 448cd3a
Show file tree
Hide file tree
Showing 23 changed files with 1,051 additions and 100 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@

# Ide
.idea

# Test
/tests/report

# Composer
/composer.lock
25 changes: 0 additions & 25 deletions Computed/Operation.php

This file was deleted.

70 changes: 70 additions & 0 deletions Extraction/ExtractionContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Draw\Swagger\Extraction;

use Draw\Swagger\Schema\Swagger as Schema;
use Draw\Swagger\Swagger;

class ExtractionContext implements ExtractionContextInterface
{
/**
* @var Schema
*/
private $rootSchema;

/**
* @var Swagger
*/
private $swagger;

private $parameters = array();

public function __construct(Swagger $swagger, Schema $rootSchema)
{
$this->rootSchema = $rootSchema;
$this->swagger = $swagger;
}

public function getRootSchema()
{
return $this->rootSchema;
}

public function getSwagger()
{
return $this->swagger;
}

/**
* @return mixed
*/
public function hasParameter($name)
{
return array_key_exists($name, $this->parameters);
}

public function getParameter($name, $default = null)
{
return $this->hasParameter($name) ? $this->parameters[$name] : $default;
}

public function getParameters()
{
return $this->parameters;
}

public function setParameter($name, $value)
{
$this->parameters[$name] = $value;
}

public function removeParameter($name)
{
unset($this->parameters[$name]);
}

public function setParameters(array $parameters)
{
$this->parameters = $parameters;
}
}
49 changes: 49 additions & 0 deletions Extraction/ExtractionContextInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Draw\Swagger\Extraction;

interface ExtractionContextInterface
{
/**
* @return \Draw\Swagger\Swagger
*/
public function getSwagger();

/**
* @return \Draw\Swagger\Schema\Swagger
*/
public function getRootSchema();

/**
* @return boolean
*/
public function hasParameter($name);

/**
* @param $name
* @param null $default
* @return mixed
*/
public function getParameter($name, $default = null);

/**
* @return array
*/
public function getParameters();

/**
* @param $name
* @param $value
*/
public function setParameter($name, $value);

/**
* @param $name
*/
public function removeParameter($name);

/**
* @param array $parameters
*/
public function setParameters(array $parameters);
}
16 changes: 16 additions & 0 deletions Extraction/ExtractionImpossibleException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Draw\Swagger\Extraction;

use Exception;

/**
* The extraction is impossible
*
* Class ExtractionImpossibleException
* @package Draw\Swagger\Extraction
*/
class ExtractionImpossibleException extends Exception
{

}
196 changes: 196 additions & 0 deletions Extraction/Extractor/JmsExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php

namespace Draw\Swagger\Extraction\Extractor;

use Draw\Swagger\Extraction\ExtractionContext;
use Draw\Swagger\Extraction\ExtractionContextInterface;
use Draw\Swagger\Extraction\ExtractionImpossibleException;
use Draw\Swagger\Extraction\ExtractorInterface;
use Draw\Swagger\Schema\Schema;
use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface;
use Metadata\PropertyMetadata;
use phpDocumentor\Reflection\DocBlock;
use ReflectionClass;

class JmsExtractor implements ExtractorInterface
{
/**
* @var MetadataFactoryInterface
*/
private $factory;

/**
* @var PropertyNamingStrategyInterface
*/
private $namingStrategy;

/**
* Constructor, requires JMS Metadata factory
*/
public function __construct(
MetadataFactoryInterface $factory,
PropertyNamingStrategyInterface $namingStrategy
) {
$this->factory = $factory;
$this->namingStrategy = $namingStrategy;
}

/**
* Return if the extractor can extract the requested data or not.
*
* @param $source
* @param $type
* @param ExtractionContextInterface $extractionContext
* @return boolean
*/
public function canExtract($source, $type, ExtractionContextInterface $extractionContext)
{
if (!$source instanceof ReflectionClass) {
return false;
}

if (!$type instanceof Schema) {
return false;
}

return !is_null($this->factory->getMetadataForClass($source->getName()));
}

/**
* Extract the requested data.
*
* The system is a incrementing extraction system. A extractor can be call before you and you must complete the
* extraction.
*
* @param ReflectionClass $reflectionClass
* @param Schema $schema
* @param ExtractionContextInterface $extractionContext
*/
public function extract($reflectionClass, $schema, ExtractionContextInterface $extractionContext)
{
if (!$this->canExtract($reflectionClass, $schema, $extractionContext)) {
throw new ExtractionImpossibleException();
}

$meta = $this->factory->getMetadataForClass($reflectionClass->getName());

$exclusionStrategies = array();

if ($groups = $extractionContext->getParameter('jms-groups', array())) {
$exclusionStrategies[] = new GroupsExclusionStrategy($groups);
}

foreach ($meta->propertyMetadata as $property => $item) {
if ($this->shouldSkipProperty($exclusionStrategies, $item)) {
continue;
}

if (is_null($item->type)) {
continue;
}

if ($type = $this->getNestedTypeInArray($item)) {
$propertySchema = new Schema();
$propertySchema->type = 'array';
$propertySchema->items = $this->extractTypeSchema($type, $extractionContext);
} else {
$propertySchema = $this->extractTypeSchema($item->type['name'], $extractionContext);
}

$name = $this->namingStrategy->translateName($item);
$schema->properties[$name] = $propertySchema;
$propertySchema->description = $this->getDescription($item);
}
}

private function extractTypeSchema($type, ExtractionContext $extractionContext)
{
$schema = new Schema();

if ($this->isPrimitive($type)) {
$schema->type = $type;
} else {
$rootSchema = $extractionContext->getRootSchema();
if (!$rootSchema->hasDefinition($type)) {
$rootSchema->addDefinition($type, $definitionSchema = new Schema());
$extractionContext->getSwagger()->extract(
new ReflectionClass($type),
$definitionSchema,
$extractionContext
);
}

$schema->ref = $rootSchema->getDefinitionReference($type);
}

return $schema;
}

private function isPrimitive($type)
{
return in_array($type, array('boolean', 'integer', 'string', 'float', 'double', 'array', 'DateTime'));
}

/**
* Check the various ways JMS describes values in arrays, and
* get the value type in the array
*
* @param PropertyMetadata $item
* @return string|null
*/
private function getNestedTypeInArray(PropertyMetadata $item)
{
if (isset($item->type['name']) && in_array($item->type['name'], array('array', 'ArrayCollection'))) {
if (isset($item->type['params'][1]['name'])) {
// E.g. array<string, MyNamespaceMyObject>
return $item->type['params'][1]['name'];
}
if (isset($item->type['params'][0]['name'])) {
// E.g. array<MyNamespaceMyObject>
return $item->type['params'][0]['name'];
}
}

return null;
}

/**
* @param PropertyMetadata $item
* @return string
*/
private function getDescription(PropertyMetadata $item)
{
$ref = new \ReflectionClass($item->class);
if ($item instanceof VirtualPropertyMetadata) {
try {
$docBlock = new DocBlock($ref->getMethod($item->getter)->getDocComment());
} catch (\ReflectionException $e) {
return '';
}
} else {
$docBlock = new DocBlock($ref->getProperty($item->name)->getDocComment());
}

return $docBlock->getShortDescription();
}

/**
* @param \JMS\Serializer\Exclusion\ExclusionStrategyInterface[] $exclusionStrategies
* @param $item
* @return bool
*/
private function shouldSkipProperty($exclusionStrategies, $item)
{
foreach ($exclusionStrategies as $strategy) {
if (true === $strategy->shouldSkipProperty($item, SerializationContext::create())) {
return true;
}
}

return false;
}
}
Loading

0 comments on commit 448cd3a

Please sign in to comment.