Skip to content

Commit e6ee71e

Browse files
authored
Merge pull request #159 from dcarbone/feature/v4-client-updates
Feature/v4 client updates
2 parents 8a9ce48 + d68291e commit e6ee71e

File tree

61 files changed

+1729
-852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1729
-852
lines changed

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
name: '${{ matrix.test-target }} - PHP ${{ matrix.php-version }} - PHPUnit ${{ matrix.phpunit-version }}'
9090
services:
9191
php-fhir-test-server:
92-
image: dancarbone/php-fhir-test-server:latest
92+
image: ghcr.io/dcarbone/php-fhir-test-server:latest
9393
ports:
9494
- '8080:8080'
9595
steps:

files/constants.php

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,28 +87,25 @@
8787
const PHPFHIR_CLASSNAME_AUTOLOADER = 'Autoloader';
8888
const PHPFHIR_CLASSNAME_VERSION_CONFIG = 'VersionConfig';
8989
const PHPFHIR_CLASSNAME_CONSTANTS = 'Constants';
90+
const PHPFHIR_CLASSNAME_FHIR_VERSION = 'FHIRVersion';
9091

9192
// Core interface names
9293
const PHPFHIR_INTERFACE_VERSION = 'VersionInterface';
9394
const PHPFHIR_INTERFACE_VERSION_CONFIG = 'VersionConfigInterface';
9495
const PHPFHIR_INTERFACE_VERSION_TYPE_MAP = 'VersionTypeMapInterface';
9596

96-
// Core enums
97-
const PHPFHIR_ENUM_VERSION = 'VersionEnum';
98-
9997
// Core exceptions
10098
const PHPFHIR_EXCEPTION_CLIENT_ABSTRACT_CLIENT = 'AbstractClientException';
10199
const PHPFHIR_EXCEPTION_CLIENT_ERROR = 'ClientErrorException';
102100
const PHPFHIR_EXCEPTION_CLIENT_UNEXPECTED_RESPONSE_CODE = 'UnexpectedResponseCodeException';
103101

104102
// Core types entities
105103
const PHPFHIR_TYPES_INTERFACE_TYPE = 'TypeInterface';
106-
const PHPFHIR_TYPES_INTERFACE_DSTU1_TYPE = 'DSTU1TypeInterface';
107-
const PHPFHIR_TYPES_INTERFACE_DSTU1_PRIMITIVE_CONTAINER_TYPE = 'DSTU1PrimitiveContainerTypeInterface';
108104
const PHPFHIR_TYPES_INTERFACE_PRIMITIVE_TYPE = 'PrimitiveTypeInterface';
109105
const PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE = 'ElementTypeInterface';
110106
const PHPFHIR_TYPES_INTERFACE_PRIMITIVE_CONTAINER_TYPE = 'PrimitiveContainerTypeInterface';
111107
const PHPFHIR_TYPES_INTERFACE_VALUE_CONTAINER_TYPE = 'ValueContainerTypeInterface';
108+
const PHPFHIR_TYPES_INTERFACE_RESOURCE_ID_TYPE = 'ResourceIDTypeInterface';
112109
const PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE = 'ResourceTypeInterface';
113110
const PHPFHIR_TYPES_INTERFACE_CONTAINED_TYPE = 'ContainedTypeInterface';
114111
const PHPFHIR_TYPES_INTERFACE_RESOURCE_CONTAINER_TYPE = 'ResourceContainerTypeInterface';
@@ -125,6 +122,7 @@
125122
const PHPFHIR_ENCODING_CLASSNAME_RESOURCE_PARSER = 'ResourceParser';
126123
const PHPFHIR_ENCODING_TRAIT_JSON_SERIALIZATION_OPTIONS = 'JSONSerializationOptionsTrait';
127124
const PHPFHIR_ENCODING_TRAIT_XML_SERIALIZATION_OPTIONS = 'XMLSerializationOptionsTrait';
125+
const PHPFHIR_ENCODING_ENUM_SERIALIZE_FORMAT = 'SerializeFormatEnum';
128126

129127
// Core client entities
130128
const PHPFHIR_CLIENT_INTERFACE_CLIENT = 'ClientInterface';
@@ -135,19 +133,15 @@
135133
const PHPFHIR_CLIENT_CLASSNAME_RESPONSE_HEADERS = 'ResponseHeaders';
136134
const PHPFHIR_CLIENT_ENUM_HTTP_METHOD = 'HTTPMethodEnum';
137135
const PHPFHIR_CLIENT_ENUM_SORT_DIRECTION = 'SortDirectionEnum';
138-
const PHPFHIR_CLIENT_ENUM_RESPONSE_FORMAT = 'ResponseFormatEnum';
139136

140-
// Version class names
137+
// Version core entities
141138
const PHPFHIR_VERSION_CLASSNAME_VERSION = 'Version';
142139
const PHPFHIR_VERSION_CLASSNAME_VERSION_CONSTANTS = 'VersionConstants';
143140
const PHPFHIR_VERSION_CLASSNAME_VERSION_TYPE_MAP = 'VersionTypeMap';
144141
const PHPFHIR_VERSION_CLASSNAME_VERSION_CLIENT = 'VersionClient';
145-
146-
// Version interface names
147142
const PHPFHIR_VERSION_INTERFACE_VERSION_CONTAINED_TYPE = 'VersionContainedTypeInterface';
148-
149-
// Version enums
150-
const PHPFHIR_VERSION_ENUM_VERSION_TYPES = 'VersionTypesEnum';
143+
const PHPFHIR_VERSION_ENUM_VERSION_RESOURCE_TYPE = 'VersionResourceTypeEnum';
144+
const PHPFHIR_VERSION_INTERFACE_RESOURCE_TYPE = 'VersionResourceTypeInterface';
151145

152146
// Validation entities
153147
const PHPFHIR_VALIDATION_CLASSNAME_VALIDATOR = 'Validator';
@@ -165,9 +159,6 @@
165159

166160
// Static test type, namespace, and class values.
167161
const PHPFHIR_TEST_CLASSNAME_CONSTANTS = PHPFHIR_CLASSNAME_CONSTANTS . 'Test';
168-
const PHPFHIR_TEST_CLASSNAME_AUTOLOADER = PHPFHIR_CLASSNAME_AUTOLOADER . 'Test';
169-
const PHPFHIR_TEST_CLASSNAME_TYPE_MAP = PHPFHIR_VERSION_CLASSNAME_VERSION_TYPE_MAP . 'Test';
170-
const PHPFHIR_TEST_CLASSNAME_VERSION_CONFIG = PHPFHIR_CLASSNAME_VERSION_CONFIG . 'Test';
171162
const PHPFHIR_TEST_ENCODING_CLASSNAME_UNSERIALIZE_CONFIG = PHPFHIR_ENCODING_CLASSNAME_UNSERIALIZE_CONFIG . 'Test';
172163
const PHPFHIR_TEST_ENCODING_CLASSNAME_SERIALIZE_CONFIG = PHPFHIR_ENCODING_CLASSNAME_SERIALIZE_CONFIG . 'Test';
173164
const PHPFHIR_TEST_ENCODING_CLASSSNAME_XML_WRITER = PHPFHIR_ENCODING_CLASSNAME_XML_WRITER . 'Test';
@@ -177,7 +168,8 @@
177168
const PHPFHIR_TEST_TRAIT_MOCK_TYPE_FIELDS = 'MockTypeFieldsTrait';
178169
const PHPFHIR_TEST_CLASSNAME_MOCK_STRING_PRIMITIVE_TYPE = 'MockStringPrimitiveType';
179170
const PHPFHIR_TEST_CLASSNAME_MOCK_ELEMENT_TYPE = 'MockElementType';
180-
const PHPFHIR_TEST_CLASSNAME_MOCK_PRIMITIVE_CONTAINER_TPYE = 'MockPrimitiveContainerType';
171+
const PHPFHIR_TEST_CLASSNAME_MOCK_PRIMITIVE_CONTAINER_TYPE = 'MockPrimitiveContainerType';
172+
const PHPFHIR_TEST_CLASSNAME_MOCK_RESOURCE_ID_TYPE = 'MockResourceIDType';
181173
const PHPFHIR_TEST_CLASSNAME_MOCK_RESOURCE_TYPE = 'MockResourceType';
182174

183175
// Test constant names

src/Builder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function writeFHIRVersionCoreEntities(string ...$versionNames): void
108108
}
109109

110110
// write version core files
111-
$this->writeCoreFiles($version->getCoreFiles(), ['version' => $version]);
111+
$this->writeCoreFiles($version->getVersionCoreFiles(), ['version' => $version]);
112112

113113
$log->endBreak("FHIR Version {$version->getName()} Core File Generation");
114114
}
@@ -190,7 +190,7 @@ public function writeFHIRVersionTestClasses(string ...$versionNames): void
190190

191191
$log->startBreak(sprintf('FHIR Version %s Test Class Generation', $version->getName()));
192192

193-
$this->writeCoreFiles($version->getVersionTestFiles(), ['version' => $version]);
193+
$this->writeCoreFiles($version->getVersionTestCoreFiles(), ['version' => $version]);
194194

195195
$log->endBreak(sprintf('FHIR Version %s Test Class Generation', $version->getName()));
196196
}

src/Builder/Import.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function __construct(string $classname, string $namespace, string $aliasN
5050
/**
5151
* @return string
5252
*/
53-
public function getClassname(): string
53+
public function getName(): string
5454
{
5555
return $this->_classname;
5656
}
@@ -87,7 +87,7 @@ public function getImportedName(): string
8787
if ($this->isAliased()) {
8888
return $this->getAliasName();
8989
}
90-
return $this->getClassname();
90+
return $this->getName();
9191
}
9292

9393
/**
@@ -123,9 +123,9 @@ public function getUseStatement(): string
123123
{
124124
$use = "use {$this->getFullyQualifiedName(false)}";
125125
if ($this->isAliased()) {
126-
$use .= " as {$this->getAliasName()}";
126+
return $use . " as {$this->getAliasName()};";
127127
}
128-
return $use . ";\n";
128+
return $use . ';';
129129
}
130130

131131
/**

src/Builder/Imports.php

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class Imports implements \Countable
3535

3636
/** @var \DCarbone\PHPFHIR\Builder\Import[] */
3737
private array $_imports = [];
38+
/** @var bool */
39+
private bool $_sorted = false;
3840

3941
/** @var int */
4042
private int $_requiredImportCount = 0;
@@ -54,59 +56,87 @@ public function __construct(Config $config, string $localNamespace, string $loca
5456
/**
5557
* @param string $namespace Namespace of referenced entity
5658
* @param string $name Name of referenced entity
57-
* @return \DCarbone\PHPFHIR\Builder\Imports
59+
* @param null|string $alias Explicit alias to use
60+
* @return \DCarbone\PHPFHIR\Builder\Import
5861
*/
59-
public function addImport(string $namespace, string $name): self
62+
public function addImport(string $namespace, string $name, null|string $alias = null): Import
6063
{
6164
// ensure clean namespace value
6265
$namespace = trim($namespace, PHPFHIR_NAMESPACE_SEPARATOR);
6366

64-
// do not need to import sibling entities.
67+
// do not need to explicitly import same-namespace entities.
6568
$requiresImport = ($namespace !== $this->_localNamespace);
69+
if ($requiresImport) {
70+
$this->_requiredImportCount++;
71+
}
72+
73+
// check if one with the same name or explicit alias already exists.
74+
$current = match ($alias) {
75+
null => $this->_imports[$name] ?? null,
76+
default => $this->_imports[$alias] ?? null,
77+
};
6678

67-
if (isset($this->_imports[$name])) {
68-
// if we have already seen this type, move on.
69-
if ($this->_imports[$name]->getNamespace() === $namespace) {
70-
return $this;
79+
// if match found...
80+
if ($current) {
81+
// ...and is an exact match, return it
82+
if ($current->getNamespace() === $namespace) {
83+
return $current;
7184
}
72-
// if there is a conflicting imported type here...
73-
$aliasName = $this->_findNextAliasName($name);
74-
$this->_imports[$aliasName] = new Import($name, $namespace, $aliasName, $requiresImport);
85+
86+
// otherwise, if alias was explicitly provided, bail out now as this indicates faulty logic somewhere
87+
// along the line.
88+
if (null !== $alias) {
89+
throw new \LogicException(sprintf(
90+
'Explicit alias "%s" for type "%s" at namespace "%s" collides with alias for type "%s" at namespace "%s".',
91+
$alias,
92+
$name,
93+
$namespace,
94+
$current->getName(),
95+
$current->getNamespace(),
96+
));
97+
}
98+
99+
// otherwise, find next available alias and create import
100+
$import = new Import($name, $namespace, $this->_findNextAliasName($name), $requiresImport);
75101
} else if ($name === $this->_localName && $namespace != $this->_localNamespace) {
76102
// if the referenced type has the same name but exists in a different namespace, alias it.
77-
$aliasName = $this->_findNextAliasName($name);
78-
$this->_imports[$aliasName] = new Import($name, $namespace, $aliasName, $requiresImport);
103+
$import = new Import($name, $namespace, $alias ?? $this->_findNextAliasName($name), $requiresImport);
79104
} else {
80105
// otherwise, go ahead and add to map.
81-
$this->_imports[$name] = new Import($name, $namespace, '', $requiresImport);
106+
$import = new Import($name, $namespace, $alias ?? '', $requiresImport);
82107
}
83108

84-
if ($requiresImport) {
85-
$this->_requiredImportCount++;
86-
}
109+
// add new import to local map
110+
$this->_sorted = false;
111+
$this->_imports[$import->getImportedName()] = $import;
87112

88-
uasort(
89-
$this->_imports,
90-
function (Import $a, Import $b) {
91-
return strnatcasecmp($a->getFullyQualifiedName(false), $b->getFullyQualifiedName(false));
92-
}
93-
);
113+
return $import;
114+
}
94115

95-
return $this;
116+
/**
117+
* Add specific core file to imported list with optional explicit alias.
118+
*
119+
* @param \DCarbone\PHPFHIR\CoreFile $coreFile
120+
* @param string|null $alias
121+
* @return \DCarbone\PHPFHIR\Builder\Import
122+
*/
123+
public function addCoreFileImport(CoreFile $coreFile, null|string $alias = null): Import
124+
{
125+
return $this->addImport($coreFile->getFullyQualifiedNamespace(false), $coreFile->getEntityName(), $alias);
96126
}
97127

98-
public function addCoreFileImports(CoreFile ...$coreFile): self
128+
public function addCoreFileImports(CoreFile ...$coreFiles): self
99129
{
100-
foreach ($coreFile as $cf) {
101-
$this->addImport($cf->getFullyQualifiedNamespace(false), $cf->getEntityName());
130+
foreach ($coreFiles as $cf) {
131+
$this->addCoreFileImport($cf);
102132
}
103133
return $this;
104134
}
105135

106136
public function addCoreFileImportsByName(string ...$entityNames): self
107137
{
108138
foreach ($entityNames as $en) {
109-
$this->addCoreFileImports(
139+
$this->addCoreFileImport(
110140
$this->_config->getCoreFiles()->getCoreFileByEntityName($en),
111141
);
112142
}
@@ -116,7 +146,7 @@ public function addCoreFileImportsByName(string ...$entityNames): self
116146
public function addVersionCoreFileImportsByName(Version $version, string ...$entityNames): self
117147
{
118148
foreach ($entityNames as $en) {
119-
$coreFile = $version->getCoreFiles()->getCoreFileByEntityName($en);
149+
$coreFile = $version->getVersionCoreFiles()->getCoreFileByEntityName($en);
120150
$this->addImport($coreFile->getNamespace(), $coreFile->getEntityName());
121151
}
122152
return $this;
@@ -135,6 +165,7 @@ public function addVersionTypeImports(Type ...$types): self
135165
*/
136166
public function getIterator(): iterable
137167
{
168+
$this->_sort();
138169
return new \ArrayIterator($this->_imports);
139170
}
140171

@@ -168,8 +199,9 @@ public function getImportByType(Type $type): Import
168199
*/
169200
public function getImportByClassAndNamespace(string $classname, string $namespace): null|Import
170201
{
202+
$this->_sort();
171203
foreach ($this->_imports as $import) {
172-
if ($import->getNamespace() === $namespace && $import->getClassname() === $classname) {
204+
if ($import->getNamespace() === $namespace && $import->getName() === $classname) {
173205
return $import;
174206
}
175207
}
@@ -208,4 +240,18 @@ private function _findNextAliasName(string $classname): string
208240
}
209241
return $aliasName;
210242
}
243+
244+
private function _sort(): void
245+
{
246+
if ($this->_sorted) {
247+
return;
248+
}
249+
uasort(
250+
$this->_imports,
251+
function (Import $a, Import $b) {
252+
return strnatcasecmp($a->getFullyQualifiedName(false), $b->getFullyQualifiedName(false));
253+
}
254+
);
255+
$this->_sorted = true;
256+
}
211257
}

src/Config.php

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* limitations under the License.
1919
*/
2020

21+
use DCarbone\PHPFHIR\Config\VersionConfig;
2122
use DCarbone\PHPFHIR\Utilities\FileUtils;
2223
use DCarbone\PHPFHIR\Utilities\NameUtils;
2324
use Psr\Log\LoggerAwareInterface;
@@ -76,7 +77,7 @@ class Config implements LoggerAwareInterface
7677

7778
/**
7879
* @param string $libraryPath
79-
* @param iterable $versions
80+
* @param array[]|\DCarbone\PHPFHIR\Config\VersionConfig[] $versions Array of VersionConfig maps or intsances.
8081
* @param string $libraryNamespacePrefix
8182
* @param null|string $testsPath
8283
* @param string $testNamespacePrefix
@@ -149,11 +150,11 @@ public static function fromArray(array $data): Config
149150
if (!isset($data['libraryPath'])) {
150151
throw new \InvalidArgumentException('Key "libraryPath" is required.');
151152
}
152-
if (isset($data['versions']) && !is_iterable($data['versions'])) {
153+
if (!isset($data['versions']) || !is_iterable($data['versions'])) {
153154
throw new \InvalidArgumentException('Key "versions" must be iterable.');
154155
}
155156
if (isset($data['logger']) && !($data['logger'] instanceof LoggerInterface)) {
156-
throw new \InvalidArgumentException('Key "logger" must be an instance of Psr\Log\LoggerInterface');
157+
throw new \InvalidArgumentException(sprintf('Key "logger" must be undefined or be an instance of %s', LoggerInterface::class));
157158
}
158159
return new Config(
159160
libraryPath: $data['libraryPath'],
@@ -257,27 +258,17 @@ public function setLibraryNamespacePrefix(string $libraryNamespacePrefix): self
257258
}
258259

259260
/**
260-
* @param array|\DCarbone\PHPFHIR\Version $version
261+
* @param array|\DCarbone\PHPFHIR\Config\VersionConfig $versionConfig
261262
* @return self
262263
*/
263-
public function addVersion(array|Version $version): self
264+
public function addVersion(array|VersionConfig $versionConfig): self
264265
{
265-
if (is_array($version)) {
266-
if (!isset($version['name'])) {
267-
throw new \InvalidArgumentException('Version name is required');
268-
}
269-
if (!isset($version['schemaPath'])) {
270-
throw new \InvalidArgumentException('Path to schemas for version is required');
271-
}
272-
$version = new Version(
273-
config: $this,
274-
name: $version['name'],
275-
schemaPath: $version['schemaPath'],
276-
namespace: $version['namespace'] ?? null,
277-
defaultConfig: $version['defaultConfig'] ?? null,
278-
);
266+
if (is_array($versionConfig)) {
267+
$versionConfig = VersionConfig::fromArray($versionConfig);
279268
}
280269

270+
$version = new Version(config: $this, versionConfig: $versionConfig);
271+
281272
// ensure unique version name
282273
if (isset($this->_versions[$version->getName()])) {
283274
throw new \InvalidArgumentException(sprintf('Version "%s" has already been added.', $version->getName()));
@@ -301,7 +292,7 @@ public function addVersion(array|Version $version): self
301292
}
302293

303294
/**
304-
* @param iterable<array|\DCarbone\PHPFHIR\Version> $versions Iterable containing Versions or Version configuration maps.
295+
* @param iterable<array|\DCarbone\PHPFHIR\Config\VersionConfig> $versions
305296
* @return self
306297
*/
307298
public function setVersions(iterable $versions): self

0 commit comments

Comments
 (0)