2017-01-14 17:36:56 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of the NelmioApiDocBundle package.
|
|
|
|
*
|
|
|
|
* (c) Nelmio
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Nelmio\ApiDocBundle\Model;
|
|
|
|
|
|
|
|
use EXSyst\Component\Swagger\Schema;
|
|
|
|
use EXSyst\Component\Swagger\Swagger;
|
|
|
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
|
|
|
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
|
|
|
|
use Symfony\Component\PropertyInfo\Type;
|
|
|
|
|
|
|
|
final class ModelRegistry
|
|
|
|
{
|
2018-06-10 09:56:38 +02:00
|
|
|
private $alternativeNames = [];
|
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
private $unregistered = [];
|
2017-12-22 17:42:18 +00:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
private $models = [];
|
2017-12-22 17:42:18 +00:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
private $names = [];
|
2017-12-22 17:42:18 +00:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
private $modelDescribers = [];
|
2017-12-22 17:42:18 +00:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
private $api;
|
|
|
|
|
|
|
|
/**
|
2018-01-04 11:34:23 +01:00
|
|
|
* @param ModelDescriberInterface[]|iterable $modelDescribers
|
2017-01-14 17:36:56 +01:00
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
*/
|
2018-06-10 09:56:38 +02:00
|
|
|
public function __construct($modelDescribers, Swagger $api, array $alternativeNames = [])
|
2017-01-14 17:36:56 +01:00
|
|
|
{
|
|
|
|
$this->modelDescribers = $modelDescribers;
|
|
|
|
$this->api = $api;
|
2018-10-10 17:24:13 +02:00
|
|
|
$this->alternativeNames = []; // last rule wins
|
2018-08-26 22:15:44 +02:00
|
|
|
|
2018-10-10 17:24:13 +02:00
|
|
|
foreach (array_reverse($alternativeNames) as $alternativeName => $criteria) {
|
|
|
|
$this->alternativeNames[] = $model = new Model(new Type('object', false, $criteria['type']), $criteria['groups']);
|
|
|
|
$this->names[$model->getHash()] = $alternativeName;
|
|
|
|
$this->api->getDefinitions()->get($alternativeName);
|
2018-08-26 22:15:44 +02:00
|
|
|
}
|
2017-01-14 17:36:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function register(Model $model): string
|
|
|
|
{
|
|
|
|
$hash = $model->getHash();
|
2018-10-10 17:24:13 +02:00
|
|
|
if (!isset($this->models[$hash])) {
|
|
|
|
$this->models[$hash] = $model;
|
|
|
|
$this->unregistered[] = $hash;
|
|
|
|
}
|
|
|
|
if (!isset($this->names[$hash])) {
|
|
|
|
$this->names[$hash] = $this->generateModelName($model);
|
2017-01-14 17:36:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reserve the name
|
2018-10-10 17:24:13 +02:00
|
|
|
$this->api->getDefinitions()->get($this->names[$hash]);
|
2017-01-14 17:36:56 +01:00
|
|
|
|
2018-10-10 17:24:13 +02:00
|
|
|
return '#/definitions/'.$this->names[$hash];
|
2017-01-14 17:36:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
public function registerDefinitions()
|
|
|
|
{
|
|
|
|
while (count($this->unregistered)) {
|
|
|
|
$tmp = [];
|
|
|
|
foreach ($this->unregistered as $hash) {
|
|
|
|
$tmp[$this->names[$hash]] = $this->models[$hash];
|
|
|
|
}
|
|
|
|
$this->unregistered = [];
|
|
|
|
|
|
|
|
foreach ($tmp as $name => $model) {
|
|
|
|
$schema = null;
|
|
|
|
foreach ($this->modelDescribers as $modelDescriber) {
|
|
|
|
if ($modelDescriber instanceof ModelRegistryAwareInterface) {
|
|
|
|
$modelDescriber->setModelRegistry($this);
|
|
|
|
}
|
|
|
|
if ($modelDescriber->supports($model)) {
|
|
|
|
$schema = new Schema();
|
|
|
|
$modelDescriber->describe($model, $schema);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (null === $schema) {
|
|
|
|
throw new \LogicException(sprintf('Schema of type "%s" can\'t be generated, no describer supports it.', $this->typeToString($model->getType())));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->api->getDefinitions()->set($name, $schema);
|
|
|
|
}
|
2018-11-04 12:24:45 +01:00
|
|
|
}
|
2018-10-10 17:24:13 +02:00
|
|
|
|
2018-11-04 12:24:45 +01:00
|
|
|
if (empty($this->unregistered) && !empty($this->alternativeNames)) {
|
|
|
|
foreach ($this->alternativeNames as $model) {
|
|
|
|
$this->register($model);
|
2018-10-10 17:24:13 +02:00
|
|
|
}
|
2018-11-04 12:24:45 +01:00
|
|
|
$this->alternativeNames = [];
|
|
|
|
$this->registerDefinitions();
|
2017-01-14 17:36:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function generateModelName(Model $model): string
|
|
|
|
{
|
|
|
|
$definitions = $this->api->getDefinitions();
|
2018-06-10 09:56:38 +02:00
|
|
|
|
2018-08-26 22:15:44 +02:00
|
|
|
$name = $base = $this->getTypeShortName($model->getType());
|
2018-06-10 09:56:38 +02:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
$i = 1;
|
|
|
|
while ($definitions->has($name)) {
|
|
|
|
++$i;
|
|
|
|
$name = $base.$i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:37:41 +01:00
|
|
|
private function getTypeShortName(Type $type): string
|
2017-01-14 17:36:56 +01:00
|
|
|
{
|
|
|
|
if (null !== $type->getCollectionValueType()) {
|
|
|
|
return $this->getTypeShortName($type->getCollectionValueType()).'[]';
|
|
|
|
}
|
2017-03-17 19:37:41 +01:00
|
|
|
|
2017-01-14 17:36:56 +01:00
|
|
|
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
|
|
|
|
$parts = explode('\\', $type->getClassName());
|
|
|
|
|
|
|
|
return end($parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $type->getBuiltinType();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function typeToString(Type $type): string
|
|
|
|
{
|
|
|
|
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
|
|
|
|
return $type->getClassName();
|
|
|
|
} elseif ($type->isCollection()) {
|
|
|
|
if (null !== $type->getCollectionValueType()) {
|
|
|
|
return $this->typeToString($type->getCollectionValueType()).'[]';
|
|
|
|
} else {
|
|
|
|
return 'mixed[]';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return $type->getBuiltinType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|