Skip to content

Add support for php 8.1 enums #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ parameters:
# all these $fqsen errors indicate the need for a decorator class around PhpParser\Node to hold the public $fqsen that Reflection is giving it)
#
# src/phpDocumentor/Reflection/NodeVisitor/ElementNameResolver.php
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Class_\|PhpParser\\Node\\Stmt\\Interface_\|PhpParser\\Node\\Stmt\\Trait_::\$fqsen#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Class_\|PhpParser\\Node\\Stmt\\Enum_\|PhpParser\\Node\\Stmt\\Interface_\|PhpParser\\Node\\Stmt\\Trait_::\$fqsen#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Namespace_::\$fqsen\.#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Enum_::\$fqsen\.#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\EnumCase::\$fqsen\.#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Interface_::\$fqsen\.#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\Function_::\$fqsen\.#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\\ClassMethod::\$fqsen\.#'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\EnumCase;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Namespace_;
Expand Down Expand Up @@ -58,6 +60,8 @@ public function leaveNode(Node $node)
switch (get_class($node)) {
case Namespace_::class:
case Class_::class:
case Enum_::class:
case EnumCase::class:
case ClassMethod::class:
case Trait_::class:
case PropertyProperty::class:
Expand Down Expand Up @@ -98,6 +102,7 @@ public function enterNode(Node $node): ?int
case Class_::class:
case Trait_::class:
case Interface_::class:
case Enum_::class:
if (empty($node->name)) {
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}
Expand Down Expand Up @@ -128,6 +133,10 @@ public function enterNode(Node $node): ?int
$this->parts->push('::$' . $node->name);
$node->fqsen = new Fqsen($this->buildName());
break;
case EnumCase::class:
$this->parts->push('::' . $node->name);
$node->fqsen = new Fqsen($this->buildName());
break;
}

return null;
Expand Down
57 changes: 57 additions & 0 deletions src/phpDocumentor/Reflection/Php/EnumCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace phpDocumentor\Reflection\Php;

use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Element;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Location;

final class EnumCase implements Element
{
/** @var Fqsen */
private $fqsen;

/** @var DocBlock|null */
private $docBlock;
/** @var Location|null */
private $location;

/** @var string|null */
private $value;

public function __construct(Fqsen $fqsen, ?DocBlock $docBlock, ?Location $location = null, ?string $value = null)
{
$this->fqsen = $fqsen;
$this->docBlock = $docBlock;
$this->location = $location;
$this->value = $value;
}

public function getFqsen(): Fqsen
{
return $this->fqsen;
}

public function getName(): string
{
return $this->fqsen->getName();
}

public function getDocBlock(): ?DocBlock
{
return $this->docBlock;
}

public function getLocation(): ?Location
{
return $this->location;
}

public function getValue(): ?string
{
return $this->value;
}
}
153 changes: 153 additions & 0 deletions src/phpDocumentor/Reflection/Php/Enum_.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\Php;

use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Element;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Location;
use phpDocumentor\Reflection\Type;

final class Enum_ implements Element
{
/** @var Fqsen Full Qualified Structural Element Name */
private $fqsen;

/** @var DocBlock|null */
private $docBlock;

/** @var Location|null */
private $location;

/** @var EnumCase[] */
private $cases = [];

/** @var array<string, Fqsen> */
private $implements = [];

/** @var array<string, Method> */
private $methods = [];

/** @var array<string, Fqsen> */
private $usedTraits = [];

/** @var Type|null */
private $backedType;

public function __construct(
Fqsen $fqsen,
?Type $backedType,
?DocBlock $docBlock = null,
?Location $location = null
) {
if ($location === null) {
$location = new Location(-1);
}

$this->fqsen = $fqsen;
$this->docBlock = $docBlock;
$this->location = $location;
$this->backedType = $backedType;
}

public function getFqsen(): Fqsen
{
return $this->fqsen;
}

public function getName(): string
{
return $this->fqsen->getName();
}

public function getDocBlock(): ?DocBlock
{
return $this->docBlock;
}

public function getLocation(): ?Location
{
return $this->location;
}

public function addCase(EnumCase $case): void
{
$this->cases[(string) $case->getFqsen()] = $case;
}

/** @return EnumCase[] */
public function getCases(): array
{
return $this->cases;
}

/**
* Returns the interfaces this enum is implementing.
*
* @return Fqsen[]
*/
public function getInterfaces(): array
{
return $this->implements;
}

/**
* Add an interface Fqsen this enum is implementing.
*/
public function addInterface(Fqsen $interface): void
{
$this->implements[(string) $interface] = $interface;
}

/**
* Returns the methods of this enum.
*
* @return Method[]
*/
public function getMethods(): array
{
return $this->methods;
}

/**
* Add a method to this enum.
*/
public function addMethod(Method $method): void
{
$this->methods[(string) $method->getFqsen()] = $method;
}

/**
* Returns the traits used by this enum.
*
* @return Fqsen[]
*/
public function getUsedTraits(): array
{
return $this->usedTraits;
}

/**
* Add trait fqsen used by this enum.
*/
public function addUsedTrait(Fqsen $fqsen): void
{
$this->usedTraits[(string) $fqsen] = $fqsen;
}

public function getBackedType(): ?Type
{
return $this->backedType;
}
}
48 changes: 48 additions & 0 deletions src/phpDocumentor/Reflection/Php/Factory/EnumCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace phpDocumentor\Reflection\Php\Factory;

use phpDocumentor\Reflection\DocBlockFactoryInterface;
use phpDocumentor\Reflection\Location;
use phpDocumentor\Reflection\Php\Enum_ as EnumElement;
use phpDocumentor\Reflection\Php\EnumCase as EnumCaseElement;
use phpDocumentor\Reflection\Php\StrategyContainer;
use PhpParser\Node\Stmt\EnumCase as EnumCaseNode;
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;

use function assert;

final class EnumCase extends AbstractFactory
{
/** @var PrettyPrinter */
private $prettyPrinter;

public function __construct(DocBlockFactoryInterface $docBlockFactory, PrettyPrinter $prettyPrinter)
{
parent::__construct($docBlockFactory);
$this->prettyPrinter = $prettyPrinter;
}

public function matches(ContextStack $context, object $object): bool
{
return $object instanceof EnumCaseNode;
}

/**
* @param EnumCaseNode $object
*/
protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): void
{
$docBlock = $this->createDocBlock($object->getDocComment(), $context->getTypeContext());
$enum = $context->peek();
assert($enum instanceof EnumElement);
$enum->addCase(new EnumCaseElement(
$object->fqsen,
$docBlock,
new Location($object->getLine()),
$object->expr !== null ? $this->prettyPrinter->prettyPrintExpr($object->expr) : null
));
}
}
65 changes: 65 additions & 0 deletions src/phpDocumentor/Reflection/Php/Factory/Enum_.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\Php\Factory;

use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Location;
use phpDocumentor\Reflection\Php\File as FileElement;
use phpDocumentor\Reflection\Php\StrategyContainer;
use PhpParser\Node\Stmt\Enum_ as EnumNode;

use function assert;

final class Enum_ extends AbstractFactory
{
public function matches(ContextStack $context, object $object): bool
{
return $object instanceof EnumNode;
}

/** @param EnumNode $object */
protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): void
{
$docBlock = $this->createDocBlock($object->getDocComment(), $context->getTypeContext());

$enum = new \phpDocumentor\Reflection\Php\Enum_(
$object->fqsen,
(new Type())->fromPhpParser($object->scalarType),
$docBlock,
new Location($object->getLine())
);

if (isset($object->implements)) {
foreach ($object->implements as $interfaceClassName) {
$enum->addInterface(
new Fqsen('\\' . $interfaceClassName->toString())
);
}
}

$file = $context->peek();
assert($file instanceof FileElement);
$file->addEnum($enum);

if (!isset($object->stmts)) {
return;
}

foreach ($object->stmts as $stmt) {
$thisContext = $context->push($enum);
$strategy = $strategies->findMatching($thisContext, $stmt);
$strategy->create($thisContext, $stmt, $strategies);
}
}
}
Loading