modelDescribers = $modelDescribers; $this->api = $api; $this->alternativeNames = array_reverse($alternativeNames); // last rule wins } public function register(Model $model): string { $hash = $model->getHash(); if (isset($this->names[$hash])) { return '#/definitions/'.$this->names[$hash]; } $this->names[$hash] = $name = $this->generateModelName($model); $this->models[$hash] = $model; $this->unregistered[] = $hash; // Reserve the name $this->api->getDefinitions()->get($name); return '#/definitions/'.$name; } /** * @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); } } } private function generateModelName(Model $model): string { $definitions = $this->api->getDefinitions(); $name = $base = $this->getAlternativeName($model) ?? $this->getTypeShortName($model->getType()); $i = 1; while ($definitions->has($name)) { ++$i; $name = $base.$i; } return $name; } /** * @param Model $model * * @return string|null */ private function getAlternativeName(Model $model) { $type = $model->getType(); foreach ($this->alternativeNames as $alternativeName => $criteria) { if ( Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() && $type->getClassName() === $criteria['type'] && $criteria['groups'] == $model->getGroups() ) { return $alternativeName; } } return null; } private function getTypeShortName(Type $type): string { if (null !== $type->getCollectionValueType()) { return $this->getTypeShortName($type->getCollectionValueType()).'[]'; } 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(); } } }