Skip to content

Commit

Permalink
popo parser add namespace context
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskapp committed Sep 23, 2023
1 parent d1266c0 commit bca367a
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 29 deletions.
53 changes: 53 additions & 0 deletions src/Parser/Context/NamespaceContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
/*
* PSX is an open source PHP framework to develop RESTful APIs.
* For the current version and information visit <https://phpsx.org>
*
* Copyright 2010-2023 Christoph Kappestein <christoph.kappestein@gmail.com>
*
* 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.
*/

namespace PSX\Schema\Parser\Context;

use PSX\Schema\Parser\ContextInterface;

/**
* NamespaceContext
*
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
* @license http://www.apache.org/licenses/LICENSE-2.0
* @link https://phpsx.org
*/
class NamespaceContext implements ContextInterface
{
private int $level;

/**
* The level variable indicates how many parts of the class name is included in the type name. I.e. for a class
* "Acme\My\SubSystem\Model" separate levels would result in the following names:
*
* - level 1 => "Model"
* - level 2 => "SubSystem_Model"
* - level 3 => "My_SubSystem_Model"
*/
public function __construct(int $level = 1)
{
$this->level = $level;
}

public function getLevel(): int
{
return $this->level;
}
}
73 changes: 44 additions & 29 deletions src/Parser/Popo.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
use PSX\Schema\DefinitionsInterface;
use PSX\Schema\Exception\ParserException;
use PSX\Schema\Format;
use PSX\Schema\Parser\Context\NamespaceContext;
use PSX\Schema\Parser\Popo\ResolverInterface;
use PSX\Schema\Parser\Popo\TypeNameBuilder;
use PSX\Schema\ParserInterface;
use PSX\Schema\Schema;
use PSX\Schema\SchemaInterface;
Expand Down Expand Up @@ -53,10 +55,12 @@
class Popo implements ParserInterface
{
private ResolverInterface $resolver;
private TypeNameBuilder $typeNameBuilder;

public function __construct()
{
$this->resolver = self::createDefaultResolver();
$this->typeNameBuilder = new TypeNameBuilder();
}

/**
Expand All @@ -67,20 +71,21 @@ public function parse(string $schema, ?ContextInterface $context = null): Schema
$className = str_replace('.', '\\', $schema);
$definitions = new Definitions();

$this->parseClass($className, $definitions);
$this->parseClass($className, $definitions, $context);

$name = (new ReflectionClass($className))->getShortName();
$name = $this->getTypeName(new ReflectionClass($className), $context);
$type = TypeFactory::getReference($name);

return new Schema($type, $definitions);
}

protected function parseClass(string $className, DefinitionsInterface $definitions): TypeInterface
protected function parseClass(string $className, DefinitionsInterface $definitions, ?ContextInterface $context = null): TypeInterface
{
$class = new ReflectionClass($className);

if ($definitions->hasType($class->getShortName())) {
return $definitions->getType($class->getShortName());
$typeName = $this->getTypeName($class, $context);
if ($definitions->hasType($typeName)) {
return $definitions->getType($typeName);
}

$type = $this->resolver->resolveClass($class);
Expand All @@ -90,14 +95,14 @@ protected function parseClass(string $className, DefinitionsInterface $definitio
$annotations[] = $attribute->newInstance();
}

$definitions->addType($class->getShortName(), $type);
$definitions->addType($typeName, $type);

if ($type instanceof StructType) {
$parent = $class->getParentClass();
if ($parent instanceof \ReflectionClass) {
$extends = $this->parseClass($parent->getName(), $definitions);
if ($extends instanceof StructType) {
$type->setExtends($parent->getShortName());
$type->setExtends($this->getTypeName($parent, $context));
}
}
}
Expand All @@ -108,12 +113,12 @@ protected function parseClass(string $className, DefinitionsInterface $definitio

if ($type instanceof StructType) {
$this->parseStructAnnotations($annotations, $type);
$this->parseProperties($class, $type, $definitions);
$this->parseProperties($class, $type, $definitions, $context);
} elseif ($type instanceof MapType) {
$this->parseMapAnnotations($annotations, $type);
$this->parseReferences($type, $definitions);
$this->parseReferences($type, $definitions, $context);
} elseif ($type instanceof ReferenceType) {
$this->parseReferences($type, $definitions);
$this->parseReferences($type, $definitions, $context);
} else {
throw new ParserException('Could not determine class type');
}
Expand All @@ -123,7 +128,7 @@ protected function parseClass(string $className, DefinitionsInterface $definitio
return $type;
}

private function parseProperties(ReflectionClass $class, StructType $property, DefinitionsInterface $definitions)
private function parseProperties(ReflectionClass $class, StructType $property, DefinitionsInterface $definitions, ?ContextInterface $context): void
{
$properties = Popo\ObjectReader::getProperties($class);
$mapping = [];
Expand All @@ -140,7 +145,7 @@ private function parseProperties(ReflectionClass $class, StructType $property, D

$type = $this->parseProperty($reflection);
if ($type instanceof TypeInterface) {
$this->parseReferences($type, $definitions);
$this->parseReferences($type, $definitions, $context);

$property->addProperty($key, $type);
}
Expand Down Expand Up @@ -189,7 +194,7 @@ private function parseProperty(\ReflectionProperty $reflection): ?TypeInterface
return $type;
}

private function parseCommonAnnotations(array $annotations, TypeAbstract $type)
private function parseCommonAnnotations(array $annotations, TypeAbstract $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\Title) {
Expand All @@ -206,7 +211,7 @@ private function parseCommonAnnotations(array $annotations, TypeAbstract $type)
}
}

private function parseScalarAnnotations(array $annotations, ScalarType $type)
private function parseScalarAnnotations(array $annotations, ScalarType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\Format) {
Expand All @@ -220,7 +225,7 @@ private function parseScalarAnnotations(array $annotations, ScalarType $type)
}
}

private function parseStructAnnotations(array $annotations, StructType $type)
private function parseStructAnnotations(array $annotations, StructType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\Required) {
Expand All @@ -229,7 +234,7 @@ private function parseStructAnnotations(array $annotations, StructType $type)
}
}

private function parseMapAnnotations(array $annotations, MapType $type)
private function parseMapAnnotations(array $annotations, MapType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\MinProperties) {
Expand All @@ -240,7 +245,7 @@ private function parseMapAnnotations(array $annotations, MapType $type)
}
}

private function parseArrayAnnotations(array $annotations, ArrayType $type)
private function parseArrayAnnotations(array $annotations, ArrayType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\MinItems) {
Expand All @@ -253,7 +258,7 @@ private function parseArrayAnnotations(array $annotations, ArrayType $type)
}
}

private function parseStringAnnotations(array $annotations, StringType $type)
private function parseStringAnnotations(array $annotations, StringType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\MinLength) {
Expand All @@ -271,7 +276,7 @@ private function parseStringAnnotations(array $annotations, StringType $type)
}
}

private function parseNumberAnnotations(array $annotations, NumberType $type)
private function parseNumberAnnotations(array $annotations, NumberType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\Minimum) {
Expand All @@ -288,7 +293,7 @@ private function parseNumberAnnotations(array $annotations, NumberType $type)
}
}

private function parseUnionAnnotations(array $annotations, UnionType $type)
private function parseUnionAnnotations(array $annotations, UnionType $type): void
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Attribute\Discriminator) {
Expand All @@ -297,43 +302,43 @@ private function parseUnionAnnotations(array $annotations, UnionType $type)
}
}

private function parseReferences(TypeInterface $type, DefinitionsInterface $definitions)
private function parseReferences(TypeInterface $type, DefinitionsInterface $definitions, ?ContextInterface $context): void
{
if ($type instanceof MapType) {
$additionalProperties = $type->getAdditionalProperties();
if ($additionalProperties instanceof TypeInterface) {
$this->parseReferences($additionalProperties, $definitions);
$this->parseReferences($additionalProperties, $definitions, $context);
}
} elseif ($type instanceof ArrayType) {
$items = $type->getItems();
if ($items instanceof TypeInterface) {
$this->parseReferences($items, $definitions);
$this->parseReferences($items, $definitions, $context);
}
} elseif ($type instanceof UnionType) {
$items = $type->getOneOf();
foreach ($items as $item) {
if ($item instanceof ReferenceType) {
$this->parseReferences($item, $definitions);
$this->parseReferences($item, $definitions, $context);
}
}
} elseif ($type instanceof IntersectionType) {
$items = $type->getAllOf();
foreach ($items as $item) {
if ($item instanceof ReferenceType) {
$this->parseReferences($item, $definitions);
$this->parseReferences($item, $definitions, $context);
}
}
} elseif ($type instanceof ReferenceType) {
$this->parseRef($type, $definitions);
$this->parseRef($type, $definitions, $context);
}
}

private function parseRef(ReferenceType $type, DefinitionsInterface $definitions)
private function parseRef(ReferenceType $type, DefinitionsInterface $definitions, ?ContextInterface $context): void
{
$className = $type->getRef();
try {
$reflection = new ReflectionClass($className);
$type->setRef($reflection->getShortName());
$type->setRef($this->getTypeName($reflection, $context));

$this->parseClass($className, $definitions);
} catch (\ReflectionException $e) {
Expand All @@ -346,7 +351,7 @@ private function parseRef(ReferenceType $type, DefinitionsInterface $definitions
foreach ($template as $key => $className) {
try {
$reflection = new ReflectionClass($className);
$result[$key] = $reflection->getShortName();
$result[$key] = $this->getTypeName($reflection, $context);
$this->parseClass($className, $definitions);
} catch (\ReflectionException $e) {
// in this case the class does not exist
Expand All @@ -356,6 +361,16 @@ private function parseRef(ReferenceType $type, DefinitionsInterface $definitions
}
}

private function getTypeName(ReflectionClass $reflection, ?ContextInterface $context): string
{
$level = 1;
if ($context instanceof NamespaceContext) {
$level = $context->getLevel();
}

return $this->typeNameBuilder->build($reflection, $level);
}

public static function createDefaultResolver(): Popo\ResolverInterface
{
return new Popo\Resolver\Composite(
Expand Down
41 changes: 41 additions & 0 deletions src/Parser/Popo/TypeNameBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
/*
* PSX is an open source PHP framework to develop RESTful APIs.
* For the current version and information visit <https://phpsx.org>
*
* Copyright 2010-2023 Christoph Kappestein <christoph.kappestein@gmail.com>
*
* 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.
*/

namespace PSX\Schema\Parser\Popo;

/**
* TypeNameBuilder
*
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
* @license http://www.apache.org/licenses/LICENSE-2.0
* @link https://phpsx.org
*/
class TypeNameBuilder
{
public function build(\ReflectionClass $reflection, int $level): string
{
if ($level > 1) {
$parts = explode('\\', $reflection->getName());
return implode('_', array_slice($parts, $level * -1));
}

return $reflection->getShortName();
}
}
Loading

0 comments on commit bca367a

Please sign in to comment.