Skip to content

Commit

Permalink
its currently broken, but lots of work towards better xml serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
dcarbone committed Jan 28, 2025
1 parent 54227ce commit 7b94f1e
Show file tree
Hide file tree
Showing 19 changed files with 545 additions and 230 deletions.
6 changes: 5 additions & 1 deletion files/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,13 @@

// Core types entities
const PHPFHIR_TYPES_INTERFACE_TYPE = 'TypeInterface';
const PHPFHIR_TYPES_INTERFACE_PRIMITIVE_TYPE = 'PrimitiveTypeInterface';
const PHPFHIR_TYPES_INTERFACE_PRIMITIVE_CONTAINER_TYPE = 'PrimitiveContainerTypeInterface';
const PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE = 'ElementTypeInterface';
const PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE = 'ResourceTypeInterface';

const PHPFHIR_TYPES_INTERFACE_CONTAINED_TYPE = 'ContainedTypeInterface';
const PHPFHIR_TYPES_INTERFACE_COMMENT_CONTAINER = 'CommentContainerInterface';
const PHPFHIR_TYPES_INTERFACE_PRIMITIVE_TYPE = 'PrimitiveTypeInterface';
const PHPFHIR_TYPES_TRAIT_COMMENT_CONTAINER = 'CommentContainerTrait';


Expand Down
42 changes: 33 additions & 9 deletions src/Utilities/ImportUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,11 @@ public static function buildVersionTypeImports(Version $version, Type $type): vo

$typeKind = $type->getKind();

if (!$type->isAbstract()) {
if (!$type->isAbstract() && !$type->isPrimitiveOrListType()) {
$imports->addCoreFileImportsByName(
PHPFHIR_ENCODING_CLASSNAME_XML_WRITER,
PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION,
PHPFHIR_ENCODING_CLASSNAME_UNSERIALIZE_CONFIG,
PHPFHIR_ENCODING_CLASSNAME_SERIALIZE_CONFIG,
PHPFHIR_TYPES_INTERFACE_TYPE,
);
}

Expand All @@ -100,6 +98,28 @@ public static function buildVersionTypeImports(Version $version, Type $type): vo
$imports->addImport($namespace, $trait);
}

if ($type->isPrimitiveOrListType() || $type->hasPrimitiveOrListParent()) {

} else if ($type->isPrimitiveContainer() || $type->hasPrimitiveContainerParent()) {
$imports->addCoreFileImportsByName(
PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE,
);
} else if ($type->isResourceType() || $type->hasResourceTypeParent()) {
$imports->addCoreFileImportsByName(
PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE,
);
} else {
$imports->addCoreFileImportsByName(
PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE,
);
}

if ($type->isValueContainer() || $type->hasValueContainerParent()) {
$imports->addCoreFileImportsByName(
PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION,
);
}

$imports->addVersionCoreFileImportsByName(
$type->getVersion(),
PHPFHIR_VERSION_CLASSNAME_VERSION,
Expand Down Expand Up @@ -130,10 +150,6 @@ public static function buildVersionTypeImports(Version $version, Type $type): vo
);
}

if ($type->isPrimitiveOrListType() || $type->isValueContainer() || $type->isPrimitiveContainer()) {
$imports->addCoreFileImportsByName(PHPFHIR_ENCODING_TRAIT_VALUE_XML_LOCATION);
}

if ($restrictionBaseType = $type->getRestrictionBaseFHIRType()) {
$imports->addImport(
$restrictionBaseType->getFullyQualifiedNamespace(false),
Expand All @@ -147,6 +163,10 @@ public static function buildVersionTypeImports(Version $version, Type $type): vo
continue;
}

if ($property->isSerializableAsXMLAttribute()) {
$imports->addCoreFileImportsByName(PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION);
}

$ptk = $propertyType->getKind();

// if (null !== $property->getOverloadedProperty() && !$ptk->isOneOf(TypeKindEnum::PRIMITIVE, TypeKindEnum::LIST)) {
Expand All @@ -166,8 +186,12 @@ public static function buildVersionTypeImports(Version $version, Type $type): vo
$imports->addVersionCoreFileImportsByName($type->getVersion(), PHPFHIR_VERSION_CLASSNAME_VERSION);
} else {
if ($ptk === TypeKindEnum::PRIMITIVE_CONTAINER) {
$primType = $propertyType->getProperties()->getProperty(PHPFHIR_VALUE_PROPERTY_NAME)->getValueFHIRType();
$imports->addImport($primType->getFullyQualifiedNamespace(false), $primType->getClassName());
$primType = $propertyType
->getProperties()
->getProperty(PHPFHIR_VALUE_PROPERTY_NAME)->getValueFHIRType();
$imports->addImport(
$primType->getFullyQualifiedNamespace(false), $primType->getClassName()
);
}

$imports->addImport(
Expand Down
20 changes: 2 additions & 18 deletions src/Version/Definition/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,11 +500,11 @@ public function getOverloadedProperty(): null|Property
}

/**
* This method will panic if this is a primitive type. Deal with it, also make it better later.
* Returns true if this property may be represented as an XML attribute on the parent element.
*
* @return bool
*/
public function requiresXMLLocation(): bool
public function isSerializableAsXMLAttribute(): bool
{
$propType = $this->getValueFHIRType();
if (null === $propType || $this->isCollection()) {
Expand All @@ -515,22 +515,6 @@ public function requiresXMLLocation(): bool
|| $propType->isPrimitiveOrListType();
}

/**
* @return string
*/
public function defaultXMLLocationEnumValue(): string
{
if (!$this->requiresXMLLocation()) {
throw new \RuntimeException(sprintf(
'Type "%s" property "%s" does not support XML location setting',
$this->getMemberOf()->getFHIRName(),
$this->getName(),
));
}

return $this->getValueFHIRType()->isValueContainer() ? 'ELEMENT' : 'ATTRIBUTE';
}

/**
* @return string
*/
Expand Down
63 changes: 46 additions & 17 deletions src/Version/Definition/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,15 @@ public function hasPrimitiveContainerParent(): bool
*/
public function isResourceType(): bool
{
return str_contains($this->getFullyQualifiedNamespace(false), '\\FHIRResource\\');
return $this->_fhirName === 'Resource'
|| $this->_fhirName === 'DomainResource'
|| str_contains($this->getFullyQualifiedNamespace(false), '\\FHIRResource\\')
|| str_contains($this->getFullyQualifiedNamespace(false), '\\FHIRDomainResource\\');
}

public function hasResourceTypeParent(): bool
{
return $this->hasParent() && $this->_parentType->isResourceType();
}

/**
Expand Down Expand Up @@ -776,6 +784,14 @@ public function setContainedType(bool $containedType): Type
return $this;
}

/**
* @return bool
*/
public function hasContainedTypeParent(): bool
{
return $this->hasParent() && $this->_parentType->isContainedType();
}

/**
* @return bool
*/
Expand Down Expand Up @@ -817,30 +833,43 @@ public function hasValueContainerParent(): bool
public function getDirectlyImplementedInterfaces(): array
{
$interfaces = [];
$parentType = $this->getParentType();
$coreFiles = $this->_version->getConfig()->getCoreFiles();
$versionCoreFiles = $this->_version->getCoreFiles();

if (null === $parentType) {
if ($this->isCommentContainer()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_COMMENT_CONTAINER] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_COMMENT_CONTAINER)
->getFullyQualifiedNamespace(false);
}
if ($this->isContainedType()) {
$interfaces[PHPFHIR_VERSION_INTERFACE_VERSION_CONTAINED_TYPE] = $versionCoreFiles
->getCoreFileByEntityName(PHPFHIR_VERSION_INTERFACE_VERSION_CONTAINED_TYPE)
->getFullyQualifiedNamespace(false);
} else if ($this->getKind() === TypeKindEnum::PRIMITIVE) {
// first, determine which base type interface it must implement
if ($this->isPrimitiveOrListType()) {
if (!$this->hasPrimitiveOrListParent()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_PRIMITIVE_TYPE] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_PRIMITIVE_TYPE)
->getFullyQualifiedNamespace(false);
} else {
$interfaces[PHPFHIR_TYPES_INTERFACE_TYPE] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_TYPE)
}
} else if ($this->isPrimitiveContainer()) {
if (!$this->hasPrimitiveContainerParent()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_PRIMITIVE_CONTAINER_TYPE] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_PRIMITIVE_CONTAINER_TYPE)
->getFullyQualifiedNamespace(false);
}
} else if ($this->isResourceType()) {
if (!$this->hasResourceTypeParent()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE)
->getFullyQualifiedNamespace(false);
}
} elseif ($this->isContainedType() && !$parentType->isContainedType()) {
} else if (!$this->hasParent()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_ELEMENT_TYPE)
->getFullyQualifiedNamespace(false);
}

// comment container types
if ($this->isCommentContainer() && !$this->hasCommentContainerParent()) {
$interfaces[PHPFHIR_TYPES_INTERFACE_COMMENT_CONTAINER] = $coreFiles
->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_COMMENT_CONTAINER)
->getFullyQualifiedNamespace(false);
}

// types that can be contained within a resource container type
if ($this->isContainedType() && !$this->hasContainedTypeParent()) {
$interfaces[PHPFHIR_VERSION_INTERFACE_VERSION_CONTAINED_TYPE] = $versionCoreFiles
->getCoreFileByEntityName(PHPFHIR_VERSION_INTERFACE_VERSION_CONTAINED_TYPE)
->getFullyQualifiedNamespace(false);
Expand Down
3 changes: 2 additions & 1 deletion template/core/encoding/enum_value_xml_location.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

enum <?php echo PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION; ?> : string
{
case ATTRIBUTE = 'attribute';
case PARENT_ATTRIBUTE = 'parent_attribute';
case LOCAL_ATTRIBUTE = 'local_attribute';
case ELEMENT = 'element';
}
<?php return ob_get_clean();
14 changes: 12 additions & 2 deletions template/core/types/interface_contained_type.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,34 @@
* limitations under the License.
*/

use DCarbone\PHPFHIR\Utilities\ImportUtils;

/** @var \DCarbone\PHPFHIR\Config $config */
/** @var \DCarbone\PHPFHIR\CoreFile $coreFile */

$coreFiles = $config->getCoreFiles();
$resourceTypeInterface = $coreFiles->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_RESOURCE_TYPE);

$imports = $coreFile->getImports();
$imports->addCoreFileImports($resourceTypeInterface);

ob_start();
echo '<?php ';?>declare(strict_types=1);

namespace <?php echo $coreFile->getFullyQualifiedNamespace(false); ?>;

<?php echo $config->getBasePHPFHIRCopyrightComment(true); ?>

<?php echo ImportUtils::compileImportStatements($imports); ?>

/**
* Interface <?php echo PHPFHIR_TYPES_INTERFACE_CONTAINED_TYPE; ?>
* Interface <?php echo $coreFile->getEntityName(); ?>

*
* This is a meta interface that must never be directly implemented by a class. It exists purely to ensure type safety
* throughout the base package.
*/
interface <?php echo PHPFHIR_TYPES_INTERFACE_CONTAINED_TYPE; ?> extends <?php echo PHPFHIR_TYPES_INTERFACE_TYPE; ?>
interface <?php echo $coreFile->getEntityName(); ?> extends <?php echo $resourceTypeInterface->getEntityName(); ?>

{
/**
Expand Down
85 changes: 85 additions & 0 deletions template/core/types/interface_element_type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php declare(strict_types=1);

/*
* Copyright 2025 Daniel Carbone ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** @var \DCarbone\PHPFHIR\Config $config */
/** @var \DCarbone\PHPFHIR\CoreFile $coreFile */

use DCarbone\PHPFHIR\Utilities\ImportUtils;

$coreFiles = $config->getCoreFiles();

$typeInterface = $coreFiles->getCoreFileByEntityName(PHPFHIR_TYPES_INTERFACE_TYPE);
$serializeConfigClass = $coreFiles->getCoreFileByEntityName(PHPFHIR_ENCODING_CLASSNAME_SERIALIZE_CONFIG);
$unserializeConfigClass = $coreFiles->getCoreFileByEntityName(PHPFHIR_ENCODING_CLASSNAME_UNSERIALIZE_CONFIG);
$xmlWriterClass = $coreFiles->getCoreFileByEntityName(PHPFHIR_ENCODING_CLASSNAME_XML_WRITER);

$imports = $coreFile->getimports();

$imports->addCoreFileImports($typeInterface, $serializeConfigClass, $unserializeConfigClass, $xmlWriterClass);

ob_start();
echo '<?php ';?>declare(strict_types=1);

namespace <?php echo $coreFile->getFullyQualifiedNamespace(false); ?>;

<?php echo $config->getBasePHPFHIRCopyrightComment(true); ?>

<?php echo ImportUtils::compileImportStatements($imports); ?>

interface <?php echo $coreFile->getEntityName(); ?> extends <?php echo $typeInterface->getEntityName(); ?>, \JsonSerializable

{
/**
* Returns the root XMLNS value found in the source. Null indicates no "xmlns" was found. Only defined when
* unserializing XML, and only used when serializing XML.
*
* @return null|string
*/
public function _getSourceXMLNS(): null|string;

/**
* @param string|\SimpleXMLElement $element
* @param null|<?php echo $coreFile->getFullyQualifiedName(true); ?> $type Instance of this class to unserialize into. If left null, a new instance will be created.
* @param null|<?php echo $unserializeConfigClass->getFullyQualifiedName(true); ?> $config
* @return static
*/
public static function xmlUnserialize(string|\SimpleXMLElement $element,
null|<?php echo $coreFile->getEntityName(); ?> $type = null,
null|<?php echo $unserializeConfigClass->getEntityName() ?> $config = null): self;

/**
* @param null|<?php echo $xmlWriterClass->getFullyQualifiedName(true); ?> $xw
* @param null|<?php echo $serializeConfigClass->getFullyQualifiedName(true); ?> $config
* @return <?php echo $xmlWriterClass->getFullyQualifiedName(true); ?>

*/
public function xmlSerialize(null|<?php echo $xmlWriterClass->getEntityName(); ?> $xw = null,
null|<?php echo $serializeConfigClass->getEntityName(); ?> $config = null): <?php echo $xmlWriterClass->getEntityName(); ?>;

/**
* @param string|array|\stdClass $json Raw or already un-encoded JSON
* @param null|<?php echo $coreFile->getFullyQualifiedName(true); ?> $type Instance of this class to unserialize into. If left null, a new instance will be created.
* @param null|<?php echo $unserializeConfigClass->getFullyQualifiedName(true); ?> $config
* @return static
*/
public static function jsonUnserialize(string|array|\stdClass $json,
null|<?php echo $coreFile->getEntityName(); ?> $type = null,
null|<?php echo $unserializeConfigClass->getEntityName(); ?> $config = null): self;
}

<?php return ob_get_clean();
Loading

0 comments on commit 7b94f1e

Please sign in to comment.