Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykbaszak committed Oct 18, 2023
1 parent 245d02a commit 1667f93
Show file tree
Hide file tree
Showing 13 changed files with 484 additions and 88 deletions.
92 changes: 92 additions & 0 deletions output.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
main: PBaszak\DedicatedMapper\Tests\assets\Dummy
classes:
PBaszak\DedicatedMapper\Tests\assets\Dummy:
attributes: { }
className: PBaszak\DedicatedMapper\Tests\assets\Dummy
options: { }
properties:
id:
attributes:
-
class: Symfony\Component\Validator\Constraints\NotBlank
arguments: { }
instance: "\\Symfony\\Component\\Validator\\Constraints\\NotBlank::__set_state(array(\n 'payload' => NULL,\n 'message' => 'This value should not be blank.',\n 'allowNull' => false,\n 'normalizer' => NULL,\n))"
-
class: Symfony\Component\Serializer\Annotation\Groups
arguments:
- test
instance: "\\Symfony\\Component\\Serializer\\Annotation\\Groups::__set_state(array(\n 'groups' => \n array (\n 0 => 'test',\n ),\n))"
name: id
options: { }
type:
types:
- string
innerType: null
nullable: false
union: false
intersection: false
class: false
collection: false
simpleObject: false
name:
attributes:
-
class: Symfony\Component\Validator\Constraints\Length
arguments:
min: 3
max: 255
instance: "\\Symfony\\Component\\Validator\\Constraints\\Length::__set_state(array(\n 'payload' => NULL,\n 'maxMessage' => 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.',\n 'minMessage' => 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.',\n 'exactMessage' => 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.',\n 'charsetMessage' => 'This value does not match the expected {{ charset }} charset.',\n 'max' => 255,\n 'min' => 3,\n 'charset' => 'UTF-8',\n 'normalizer' => NULL,\n 'countUnit' => 'codepoints',\n))"
-
class: Symfony\Component\Serializer\Annotation\Groups
arguments:
- test
instance: "\\Symfony\\Component\\Serializer\\Annotation\\Groups::__set_state(array(\n 'groups' => \n array (\n 0 => 'test',\n ),\n))"
name: name
options: { }
type:
types:
- string
innerType: null
nullable: false
union: false
intersection: false
class: false
collection: false
simpleObject: false
description:
attributes: { }
name: description
options: { }
type:
types:
- string
innerType: null
nullable: false
union: false
intersection: false
class: false
collection: false
simpleObject: false
_embedded:
attributes:
-
class: Symfony\Component\Validator\Constraints\Valid
arguments: { }
instance: "\\Symfony\\Component\\Validator\\Constraints\\Valid::__set_state(array(\n 'payload' => NULL,\n 'traverse' => true,\n))"
-
class: Symfony\Component\Serializer\Annotation\Groups
arguments:
- test
instance: "\\Symfony\\Component\\Serializer\\Annotation\\Groups::__set_state(array(\n 'groups' => \n array (\n 0 => 'test',\n ),\n))"
name: _embedded
options: { }
type:
types:
- PBaszak\DedicatedMapper\Tests\assets\EmbeddedDTO
innerType: null
nullable: false
union: false
intersection: false
class: true
collection: false
simpleObject: false
27 changes: 27 additions & 0 deletions src/Command/CreateConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace PBaszak\DedicatedMapper\Command;

use PBaszak\DedicatedMapper\Config;
use PBaszak\DedicatedMapper\Tests\assets\Dummy;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;

require_once __DIR__ . '/../../tests/assets/Dummy.php';

#[AsCommand(
name: 'mapper:create-config',
description: 'Creates mapper config for specific class.',
)]
class CreateConfig extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
file_put_contents('output.yaml', Yaml::dump((new Config(Dummy::class))->reflect()->export(), 256, 4));

return Command::SUCCESS;
}
}
35 changes: 23 additions & 12 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@

namespace PBaszak\DedicatedMapper;

use LogicException;
use PBaszak\DedicatedMapper\Reflection\ClassReflection;
use PBaszak\DedicatedMapper\Reflection\ReflectionFactory;
use PBaszak\DedicatedMapper\Reflection\Type\ClassType;
use PBaszak\DedicatedMapper\Reflection\Type\TypeFactory;

class Config
{
/** @var array<string> */
protected array $usedFiles = [];
protected ClassReflection $classReflection;
/**
* @var array<string, ClassType>
*/
protected array $classes;

/**
* @param class-string $className
*/
public function __construct(private string $className)
public function __construct(protected string $className)
{
if (!class_exists($this->className, false)) {
throw new \InvalidArgumentException(
Expand All @@ -27,30 +31,37 @@ public function __construct(private string $className)

public function reflect(): self
{
$this->classReflection = (new ReflectionFactory())->createClassReflection($this->className, null);
ClassType::$classTypes = [];
(new TypeFactory(new ReflectionFactory()))->createClassType(
new \ReflectionClass($this->className),
null
);

$this->classes = ClassType::$classTypes;

return $this;
}

public function getClassReflection(): ClassReflection
public function getMainClassType(): ClassType
{
return $this->classReflection;
return $this->classes[$this->className];
}

public function export(): array
{
if (!isset($this->classes)) {
throw new LogicException('Class reflection is not set. You need to call reflect() method first.');
}

return [
'className' => $this->className,
'classReflection' => $this->classReflection,
'usedFiles' => $this->usedFiles,
'main' => $this->className,
'classes' => array_map(fn (ClassType $class) => $class->toArray(), $this->classes),
];
}

public static function import(array $data): self
{
$config = new self($data['className']);
$config->classReflection = $data['classReflection'];
$config->usedFiles = $data['usedFiles'];

return $config;
}
Expand Down
15 changes: 14 additions & 1 deletion src/Reflection/AttributeReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

class AttributeReflection
{
public function toArray(): array
{
return array_map(
fn (object $attribute) => [
'class' => $attribute->class,
'arguments' => $attribute->arguments,
'instance' => var_export($attribute->instance, true),
],
$this->attributes->getArrayCopy()
);
}

public function __construct(
/**
* @var ClassReflection|CollectionType|PropertyReflection|SimpleObjectType $parent each attribute must have resource
Expand All @@ -20,7 +32,8 @@ public function __construct(
* @var ArrayObject<object{"class": string, "arguments": mixed[]}> $attributes
*/
protected ArrayObject $attributes,
) {}
) {
}

/**
* @return ClassReflection|CollectionType|PropertyReflection|SimpleObjectType
Expand Down
12 changes: 12 additions & 0 deletions src/Reflection/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@

use ArrayObject;
use PBaszak\DedicatedMapper\Reflection\Type\ClassType;
use PBaszak\DedicatedMapper\Utils\ToArrayTrait;
use ReflectionClass;
use Serializable;

class ClassReflection
{
public function toArray(): array
{
return [
'attributes' => $this->attributes->toArray(),
'className' => $this->reflection?->getName(),
'options' => $this->options->toArray(),
'properties' => array_map(fn (PropertyReflection $property) => $property->toArray(), $this->properties->getArrayCopy()),
];
}

public function __construct(
/**
* @var null|\ReflectionClass $reflection `null` is available for reversed mapping
Expand Down
13 changes: 12 additions & 1 deletion src/Reflection/PropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,20 @@
namespace PBaszak\DedicatedMapper\Reflection;

use PBaszak\DedicatedMapper\Reflection\Type\TypeInterface;
use PBaszak\DedicatedMapper\Utils\ToArrayTrait;

class PropertyReflection
{
public function toArray(): array
{
return [
'attributes' => $this->attributes->toArray(),
'name' => $this->name,
'options' => $this->options->toArray(),
'type' => $this->type->toArray(),
];
}

public function __construct(
/**
* @var ClassReflection $parent each property must have parent class
Expand Down Expand Up @@ -103,4 +114,4 @@ public function getReflectionParameter(): ?\ReflectionParameter
{
return $this->reflectionParameter;
}
}
}
4 changes: 2 additions & 2 deletions src/Reflection/ReflectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use ArrayObject;
use PBaszak\DedicatedMapper\Attribute\ApplyToCollectionItems;
use PBaszak\DedicatedMapper\Reflection\Type\ClassType;
use PBaszak\DedicatedMapper\Reflection\Type\TypeFactory;
use PBaszak\DedicatedMapper\Reflection\Type\TypeInterface;
use PBaszak\DedicatedMapper\Utils\GetParametersFromObject;
use ReflectionClass;
Expand Down Expand Up @@ -49,8 +50,7 @@ public function createPropertyReflection(\ReflectionProperty $reflection, ClassR
$attributes = new AttributeReflection($instance, $this->getAttributesFromReflection($reflection));
$ref->getProperty('attributes')->setValue($instance, $attributes);
$ref->getProperty('options')->setValue($instance, new Options());

// type
$ref->getProperty('type')->setValue($instance, (new TypeFactory($this))->createType($instance));

return $instance;
}
Expand Down
42 changes: 40 additions & 2 deletions src/Reflection/Type/ClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,49 @@

class ClassType implements TypeInterface
{
public static array $classTypes = [];

/**
* @param class-string $class
*/
public static function isClassTypeExists(string $class): bool
{
return isset(self::$classTypes[$class]);
}

public static function storeClassType(ClassType $instance): void
{
$class = $instance->getReflection()->getReflection()?->getName();
if (!class_exists($class, false)) {
throw new \InvalidArgumentException(sprintf('Class %s does not exists', $class));
}
self::$classTypes[$class] = $instance;
}

public static function supports(Type $type): bool
{
foreach ($type->getTypes() as $t) {
if (class_exists($t, false)) {
return true;
}
}
}

public static function create(Type $type): TypeInterface
{

}

public function toArray(): array
{
return $this->reflection?->toArray();
}

public function __construct(
/**
* @var null|ClassReflection $reflection if `null`, then it is root class
* @var ClassReflection $reflection
*/
protected null|ClassReflection $reflection = null,
protected ClassReflection $reflection,

/**
* @var PropertyReflection|TypeInterface|null $parent if `null`, then it is root class
Expand Down
17 changes: 16 additions & 1 deletion src/Reflection/Type/CollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@
use ArrayObject;
use PBaszak\DedicatedMapper\Reflection\AttributeReflection;
use PBaszak\DedicatedMapper\Reflection\PropertyReflection;
use PBaszak\DedicatedMapper\Utils\ToArrayTrait;

class CollectionType implements TypeInterface
{
public function toArray(): array
{
return [
'children' => array_map(fn (TypeInterface $child) => $child->toArray(), $this->children->getArrayCopy()),
'attributes' => $this->attributes->toArray(),
];
}

public static function supports(PropertyReflection $property, Type $type, int $depth): bool
{
return $type->isCollection();
}

public function __construct(
/**
* @var null|PropertyReflection|TypeInterface $parent
Expand All @@ -26,7 +40,8 @@ public function __construct(
* @var AttributeReflection $attributes
*/
protected AttributeReflection $attributes,
) {}
) {
}

/**
* @return null|PropertyReflection|TypeInterface
Expand Down
Loading

0 comments on commit 1667f93

Please sign in to comment.