Skip to content

Commit

Permalink
refactor: remove array_converter::configure and related methods
Browse files Browse the repository at this point in the history
  • Loading branch information
MHajoha committed Oct 23, 2024
1 parent 316218b commit e30413d
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 139 deletions.
60 changes: 14 additions & 46 deletions classes/array_converter/array_converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class array_converter {
/** @var callable[] associative array of class names to configuration hook functions for those classes */
private static array $hooks = [];

/**
* Customizes the way in which classes and their subclasses are converted from and to arrays.
*
* In the case of traits, the configuration will be applied to any classes using them.
* Configuration is done in hook functions. When converting to or from a class instance, all hook function of it and
* its superclasses and used traits are applied to a single {@see converter_config} instance.
*
* @param string $class class or trait for whom array conversion should be customized
* @param callable $hook configuration function which takes a {@see converter_config} instance as its argument,
* adds its own configuration to it, and returning nothing
* @see converter_config for the available options
*/
public static function configure(string $class, callable $hook): void {
self::$hooks[$class] = $hook;
}

/**
* Recursively converts an array to an instance of the given class.
*
Expand Down Expand Up @@ -323,46 +304,27 @@ private static function convert_to_required_type(?ReflectionNamedType $type, con
}

/**
* Calls all configuration hooks for the given class.
* Inspects the attributes on the given class, superclasses and traits and updates the given config.
*
* @param ReflectionClass $class
* @param ReflectionClass $reflect
* @param converter_config|null $config an existing config to add to or null, in which case a new one will be
* created
* @return converter_config
* @see self::configure()
*/
private static function get_config_for(ReflectionClass $class, ?converter_config $config = null): converter_config {
private static function get_config_for(ReflectionClass $reflect, ?converter_config $config = null): converter_config {
if (!$config) {
$config = new converter_config();
}

$parent = $class->getParentClass();
$parent = $reflect->getParentClass();
if ($parent) {
$config = self::get_config_for($parent, $config);
}

foreach ($class->getTraits() as $trait) {
foreach ($reflect->getTraits() as $trait) {
$config = self::get_config_for($trait, $config);
}

self::configure_from_attributes($class, $config);

$hook = self::$hooks[$class->getName()] ?? null;
if ($hook) {
$hook($config);
}

return $config;
}

/**
* Inspects the attributes on the given class and updates the given config.
*
* @param ReflectionClass $reflect
* @param converter_config $config
* @return void
*/
private static function configure_from_attributes(ReflectionClass $reflect, converter_config $config): void {
$polyattrs = $reflect->getAttributes(array_polymorphic::class);
foreach ($polyattrs as $attr) {
/** @var array_polymorphic $instance */
Expand All @@ -378,14 +340,20 @@ private static function configure_from_attributes(ReflectionClass $reflect, conv
}

foreach ($property->getAttributes(array_key::class) as $attr) {
$config->rename($property->getName(), $attr->newInstance()->key);
$config->renames[$property->getName()] = $attr->newInstance()->key;
}
foreach ($property->getAttributes(array_alias::class) as $attr) {
$config->alias($property->getName(), $attr->newInstance()->alias);
if (isset($config->aliases[$property->getName()])) {
$config->aliases[$property->getName()][] = $attr->newInstance()->alias;
} else {
$config->aliases[$property->getName()] = [$attr->newInstance()->alias];
}
}
foreach ($property->getAttributes(array_element_class::class) as $attr) {
$config->array_elements($property->getName(), $attr->newInstance()->class);
$config->elementclasses[$property->getName()] = $attr->newInstance()->class;
}
}

return $config;
}
}
94 changes: 1 addition & 93 deletions classes/array_converter/converter_config.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
namespace qtype_questionpy\array_converter;

/**
* Allows customization of conversions by {@see array_converter}.
*
* @see array_converter::configure()
* Holds customization of {@see array_converter}.
*
* @package qtype_questionpy
* @author Maximilian Haye
Expand All @@ -41,94 +39,4 @@ class converter_config {

/** @var string[] mapping from property names to the classes of their array elements */
public array $elementclasses = [];

/**
* Changes the name under which the value of a property appears in arrays.
*
* Renames differ from {@see self::alias() aliases} in that they apply to both serialization and deserialization,
* and replace the original property name.
*
* @param string $propname property whose array key to rename
* @param string $arraykey new array key
* @return $this for chaining
*/
public function rename(string $propname, string $arraykey): self {
$this->renames[$propname] = $arraykey;
return $this;
}

/**
* Adds an alias for the given property.
*
* Aliases differ from {@see self::rename() renames} in that they only apply to deserialization, and are tried in
* addition to the original property name (or rename, if any).
*
* @param string $propname property name for which the alias should be tried
* @param string $alias new alias
* @return $this for chaining
*/
public function alias(string $propname, string $alias): self {
if (isset($this->aliases[$propname])) {
$this->aliases[$propname][] = $alias;
} else {
$this->aliases[$propname] = [$alias];
}
return $this;
}

/**
* Enables polymorphic deserialization for this class, using the given key as a discriminator.
*
* The value of the given array key will determine the actual class ('variant') used for deserialization. Variants
* are registered using {@see self::variant()}.
*
* @param string $discriminator key of the array entry determining the concrete class to deserialize to.
* @return $this for chaining
*/
public function discriminate_by(string $discriminator): self {
$this->discriminator = $discriminator;
return $this;
}

/**
* Adds a variant for polymorphic deserialization.
*
* @param string $discriminator the discriminator value associated with the concrete class
* @param string $classname the concrete class to convert to when the discriminator is encountered
* @return $this for chaining
* @see self::discriminate_by()
* @see self::fallback_variant()
*/
public function variant(string $discriminator, string $classname): self {
$this->variants[$discriminator] = $classname;
return $this;
}

/**
* Sets the fallback variant for polymorphic deserialization.
*
* When a discriminator is encountered which isn't {@see variant registered}, the default behaviour is to throw an
* exception. Instead, you can register a fallback class to be used. A debugging message will still be emitted.
*
* @param string $classname the concrete class to convert to when an unknown discriminator is encountered
* @return $this for chaining
* @see self::discriminate_by()
* @see self::variant()
*/
public function fallback_variant(string $classname): self {
$this->fallbackvariant = $classname;
return $this;
}

/**
* For an array-typed property with the given name, sets the class to use for the deserialization of its elements.
*
* @param string $propname property name
* @param string $class class to deserialize to
* @return $this for chaining
*/
public function array_elements(string $propname, string $class): self {
$this->elementclasses[$propname] = $class;
return $this;
}
}

0 comments on commit e30413d

Please sign in to comment.